public async Task HttpTrigger_Model_Binding_V2CompatMode()
        {
            // We need a custom host to set this to v2 compat mode.
            using (var host = new TestFunctionHost(@"TestScripts\CSharp", Path.Combine(Path.GetTempPath(), "Functions"),
                                                   configureWebHostServices: webHostServices =>
            {
                var environment = new TestEnvironment();
                environment.SetEnvironmentVariable(EnvironmentSettingNames.FunctionsV2CompatibilityModeKey, "true");
                webHostServices.AddSingleton <IEnvironment>(_ => environment);
            },
                                                   configureScriptHostWebJobsBuilder: webJobsBuilder =>
            {
                webJobsBuilder.Services.Configure <ScriptJobHostOptions>(o =>
                {
                    // Only load the functions we care about
                    o.Functions = new[]
                    {
                        "HttpTrigger-Model-v2",
                    };
                });
            }))
            {
                (JObject req, JObject res) = await MakeModelRequest(host.HttpClient, "-v2");

                // in v2, we expect the response to have a null customEnumerable property.
                req["customEnumerable"] = null;

                Assert.True(JObject.DeepEquals(req, res), res.ToString());
            }
        }
        public async Task HostStatusReturns_IfHostJsonError()
        {
            string hostJsonPath = Path.Combine(_hostPath, ScriptConstants.HostMetadataFileName);

            // Simulate a non-empty file without a 'version'
            JObject hostConfig = JObject.FromObject(new
            {
                functionTimeout = TimeSpan.FromSeconds(30)
            });

            await File.WriteAllTextAsync(hostJsonPath, hostConfig.ToString());

            var host = new TestFunctionHost(_hostPath, _ => { });

            // Ping the status endpoint to ensure we see the exception
            HostStatus status = await host.GetHostStatusAsync();

            Assert.Equal("Error", status.State);
            Assert.Equal("Microsoft.Azure.WebJobs.Script: The host.json file is missing the required 'version' property. See https://aka.ms/functions-hostjson for steps to migrate the configuration file.", status.Errors.Single());

            // Now update the file and make sure it auto-restarts.
            hostConfig["version"] = "2.0";
            await File.WriteAllTextAsync(hostJsonPath, hostConfig.ToString());

            await TestHelpers.Await(async() =>
            {
                status = await host.GetHostStatusAsync();
                return(status.State == $"{ScriptHostState.Running}");
            });

            Assert.Null(status.Errors);
        }
        public async Task TestWarmupEndPoint_WhenHostStarts()
        {
            string testScriptPath = Path.Combine("TestScripts", "CSharp");
            string testLogPath    = Path.Combine(TestHelpers.FunctionsTestDirectory, "Logs", Guid.NewGuid().ToString(), @"Functions");
            var    settings       = new Dictionary <string, string>()
            {
                ["WEBSITE_SKU"] = "ElasticPremium"
            };
            var testEnvironment = new TestEnvironment(settings);

            _testHost = new TestFunctionHost(testScriptPath, testLogPath,
                                             configureWebHostServices: services =>
            {
                services.AddSingleton <IScriptHostBuilder, PausingScriptHostBuilder>();
                services.AddSingleton <IEnvironment>(testEnvironment);
                services.AddSingleton <IConfigureBuilder <IWebJobsBuilder> >(new DelegatedConfigureBuilder <IWebJobsBuilder>(b =>
                {
                    b.UseHostId("1234");
                    b.Services.Configure <ScriptJobHostOptions>(o => o.Functions = new[] { "ManualTrigger", "Scenarios" });
                }));
            });

            // Make sure host started properly and drain the semaphore count released
            // by inital start
            Assert.True(_hostBuild.Wait(SemaphoreWaitTimeout), "Host failed to start");

            string              uri      = "admin/warmup";
            HttpRequestMessage  request  = new HttpRequestMessage(HttpMethod.Get, uri);
            HttpResponseMessage response = await _testHost.HttpClient.SendAsync(request);

            // Better be successful
            Assert.True(response.IsSuccessStatusCode, "Warmup endpoint did not return a success status. " +
                        $"Instead found {response.StatusCode}");
        }
        public WebJobsScriptHostServiceTests()
        {
            // configure the monitor so it will fail within a couple seconds
            _healthMonitorOptions = new HostHealthMonitorOptions
            {
                HealthCheckInterval  = TimeSpan.FromMilliseconds(100),
                HealthCheckWindow    = TimeSpan.FromSeconds(1),
                HealthCheckThreshold = 5
            };
            var wrappedHealthMonitorOptions = new OptionsWrapper <HostHealthMonitorOptions>(_healthMonitorOptions);

            _mockApplicationLifetime = new Mock <IApplicationLifetime>(MockBehavior.Loose);
            _mockApplicationLifetime.Setup(p => p.StopApplication())
            .Callback(() =>
            {
                _shutdownCalled = true;
            });

            _mockEnvironment = new Mock <IEnvironment>();
            var mockServiceProvider = new Mock <IServiceProvider>(MockBehavior.Strict);

            _mockEnvironment.Setup(p => p.GetEnvironmentVariable(EnvironmentSettingNames.AzureWebsiteInstanceId)).Returns("testapp");
            _mockEnvironment.Setup(p => p.GetEnvironmentVariable(EnvironmentSettingNames.AzureWebsiteHostName)).Returns("testapp");

            var mockHostPerformanceManager = new Mock <HostPerformanceManager>(_mockEnvironment.Object, wrappedHealthMonitorOptions, mockServiceProvider.Object, null);

            mockHostPerformanceManager.Setup(p => p.PerformanceCountersExceeded(It.IsAny <Collection <string> >(), It.IsAny <ILogger>()))
            .Callback <Collection <string>, ILogger>((c, l) =>
            {
                if (_countersExceeded)
                {
                    foreach (var counter in _exceededCounters)
                    {
                        c.Add(counter);
                    }
                }
            })
            .Returns(() => _countersExceeded);

            _testHost = new TestFunctionHost(TestScriptPath, TestLogPath,
                                             configureWebHostServices: services =>
            {
                services.AddSingleton <IOptions <HostHealthMonitorOptions> >(wrappedHealthMonitorOptions);
                services.AddSingleton <IApplicationLifetime>(_mockApplicationLifetime.Object);
                services.AddSingleton <IEnvironment>(_mockEnvironment.Object);
                services.AddSingleton <HostPerformanceManager>(mockHostPerformanceManager.Object);

                services.AddSingleton <IConfigureBuilder <IWebJobsBuilder> >(new DelegatedConfigureBuilder <IWebJobsBuilder>(b =>
                {
                    b.UseHostId("1234");
                    b.Services.Configure <ScriptJobHostOptions>(o => o.Functions = new[] { "ManualTrigger", "Scenarios" });
                }));
            },
                                             configureScriptHostWebJobsBuilder: builder =>
            {
                builder.AddExtension <TestWebHookExtension>();
            });

            _scriptHostService = _testHost.JobHostServices.GetService <IScriptHostManager>() as WebJobsScriptHostService;
        }
        public async Task TestWarmupEndPoint_WhenHostStarts()
        {
            string testScriptPath = Path.Combine("TestScripts", "CSharp");
            string testLogPath    = Path.Combine(TestHelpers.FunctionsTestDirectory, "Logs", Guid.NewGuid().ToString(), @"Functions");
            var    settings       = new Dictionary <string, string>()
            {
                ["WEBSITE_SKU"] = "ElasticPremium"
            };
            var testEnvironment = new TestEnvironment(settings);

            _testHost = new TestFunctionHost(testScriptPath, testLogPath,
                                             configureWebHostServices: services =>
            {
                services.AddSingleton <IScriptHostBuilder, PausingScriptHostBuilder>();
                services.AddSingleton <IEnvironment>(testEnvironment);
                services.AddSingleton <IConfigureBuilder <IWebJobsBuilder> >(new DelegatedConfigureBuilder <IWebJobsBuilder>(b =>
                {
                    b.UseHostId("1234");
                    b.Services.Configure <ScriptJobHostOptions>(o => o.Functions = new[] { "ManualTrigger", "Scenarios" });
                }));
            });

            // Make sure host started properly and drain the semaphore count released
            // by inital start
            Assert.True(_hostBuild.Wait(SemaphoreWaitTimeout), "Host failed to start");

            // Restart needs to be a separate thread as we need to pause the restart
            // and make the warmup call during the build step.
            // The IScriptHostBuilder.BuildHost is synchronous, so creating another thread
            // ensures that we can continue other tasks
            Thread hostRestart = new Thread(async() =>
            {
                await _testHost.RestartAsync(CancellationToken.None);
            });

            hostRestart.Start();

            // Wait for restart to hit the build
            Assert.True(_hostBuild.Wait(SemaphoreWaitTimeout), "Host failed to start");

            // Let's make the warmup request and not wait for it to finish,
            // as warmup call needs the host to be running while the host is currently paused
            string                     uri          = "admin/warmup";
            HttpRequestMessage         request      = new HttpRequestMessage(HttpMethod.Get, uri);
            Task <HttpResponseMessage> responseTask = _testHost.HttpClient.SendAsync(request);

            // We wait for 5 seconds just to make sure warmup call was invoked properly
            await Task.Delay(TimeSpan.FromSeconds(5));

            // We let the host continue to make sure it starts back up properly
            _hostSetup.Release();

            // Now we can wait for the warmup call to complete
            var response = await responseTask;

            // Better be successful
            Assert.True(response.IsSuccessStatusCode, "Warmup endpoint did not return a success status. " +
                        $"Instead found {response.StatusCode}");
        }
示例#6
0
        public WebJobsScriptHostServiceTests()
        {
            string testScriptPath = @"TestScripts\CSharp";
            string testLogPath    = Path.Combine(TestHelpers.FunctionsTestDirectory, "Logs", Guid.NewGuid().ToString(), @"Functions");

            // configure the monitor so it will fail within a couple seconds
            _healthMonitorOptions = new HostHealthMonitorOptions
            {
                HealthCheckInterval  = TimeSpan.FromMilliseconds(100),
                HealthCheckWindow    = TimeSpan.FromSeconds(1),
                HealthCheckThreshold = 5
            };
            var wrappedHealthMonitorOptions = new OptionsWrapper <HostHealthMonitorOptions>(_healthMonitorOptions);

            _mockJobHostEnvironment = new Mock <IScriptJobHostEnvironment>(MockBehavior.Strict);
            _mockJobHostEnvironment.Setup(p => p.Shutdown())
            .Callback(() =>
            {
                _shutdownCalled = true;
            });

            var mockEnvironment = new Mock <IEnvironment>();

            mockEnvironment.Setup(p => p.GetEnvironmentVariable(EnvironmentSettingNames.AzureWebsiteInstanceId)).Returns("testapp");

            var mockHostPerformanceManager = new Mock <HostPerformanceManager>(mockEnvironment.Object, wrappedHealthMonitorOptions);

            mockHostPerformanceManager.Setup(p => p.IsUnderHighLoad(It.IsAny <Collection <string> >(), It.IsAny <ILogger>()))
            .Callback <Collection <string>, ILogger>((c, l) =>
            {
                if (_underHighLoad)
                {
                    foreach (var counter in _exceededCounters)
                    {
                        c.Add(counter);
                    }
                }
            })
            .Returns(() => _underHighLoad);

            _testHost = new TestFunctionHost(testScriptPath, testLogPath,
                                             configureWebHostServices: services =>
            {
                services.AddSingleton <IOptions <HostHealthMonitorOptions> >(wrappedHealthMonitorOptions);
                services.AddSingleton <IScriptJobHostEnvironment>(_mockJobHostEnvironment.Object);
                services.AddSingleton <IEnvironment>(mockEnvironment.Object);
                services.AddSingleton <HostPerformanceManager>(mockHostPerformanceManager.Object);

                services.AddSingleton <IConfigureBuilder <IWebJobsBuilder> >(new DelegatedConfigureBuilder <IWebJobsBuilder>(b =>
                {
                    b.UseHostId("1234");
                    b.Services.Configure <ScriptJobHostOptions>(o => o.Functions = new[] { "ManualTrigger", "Scenarios" });
                }));
            });

            _scriptHostService = _testHost.JobHostServices.GetService <IScriptHostManager>() as WebJobsScriptHostService;
        }
示例#7
0
        private async Task RunScenario(TestScenario scenario)
        {
            var cancellationToken = new CancellationToken();
            var host = new TestFunctionHost();

            using (var scope = host.GetScope())
            {
                await ReceiveEventsAsync(scope, scenario, cancellationToken);

                await ProcessEventsAsync(scope, cancellationToken);

                CheckState(scope, scenario);
            }
        }
示例#8
0
        private static TestFunctionHost GetHost(Action <IDictionary <string, string> > addEnvironmentVariables = null)
        {
            string scriptPath = @"TestScripts\DirectLoad\";
            string logPath    = Path.Combine(Path.GetTempPath(), @"Functions");

            var host = new TestFunctionHost(scriptPath, logPath,
                                            configureWebHostServices: s =>
            {
                IDictionary <string, string> dict = new Dictionary <string, string>();
                addEnvironmentVariables?.Invoke(dict);
                s.AddSingleton <IEnvironment>(_ => new TestEnvironment(dict));
            });

            return(host);
        }
示例#9
0
        public async Task DisposedScriptLoggerFactory_UsesFullStackTrace()
        {
            var host = new TestFunctionHost(@"TestScripts\CSharp",
                                            configureScriptHostServices: s =>
            {
                s.AddSingleton <IExtensionConfigProvider, CustomTriggerExtensionConfigProvider>();
                s.Configure <ScriptJobHostOptions>(o => o.Functions = new[] { "CustomTrigger" });
            });

            await CustomListener.RunAsync("one");

            host.Dispose();

            // In this scenario, the logger throws an exception before we enter the try/catch for the function invocation.
            var ex = await Assert.ThrowsAsync <HostDisposedException>(() => CustomListener.RunAsync("two"));

            Assert.Equal($"The host is disposed and cannot be used. Disposed object: '{typeof(ScriptLoggerFactory).FullName}'; Found IListener in stack trace: '{typeof(CustomListener).AssemblyQualifiedName}'", ex.Message);
            Assert.Contains("CustomListener.RunAsync", ex.StackTrace);
        }
示例#10
0
        public ApplicationInsightsTestFixture(string scriptRoot, string testId)
        {
            string scriptPath = Path.Combine(Environment.CurrentDirectory, scriptRoot);
            string logPath    = Path.Combine(Path.GetTempPath(), @"Functions");

            Environment.SetEnvironmentVariable(EnvironmentSettingNames.FunctionWorkerRuntime, testId);

            WebHostOptions = new ScriptApplicationHostOptions
            {
                IsSelfHost  = true,
                ScriptPath  = scriptPath,
                LogPath     = logPath,
                SecretsPath = Environment.CurrentDirectory // not used
            };

            TestHost = new TestFunctionHost(scriptPath, logPath,
                                            configureScriptHostServices: s =>
            {
                s.AddSingleton <ITelemetryChannel>(_ => Channel);
                s.Configure <ScriptJobHostOptions>(o =>
                {
                    o.Functions = new[]
                    {
                        "Scenarios",
                        "HttpTrigger-Scenarios"
                    };
                });
                s.AddSingleton <IMetricsLogger>(_ => MetricsLogger);
            },
                                            configureScriptHostAppConfiguration: configurationBuilder =>
            {
                configurationBuilder.AddInMemoryCollection(new Dictionary <string, string>
                {
                    [EnvironmentSettingNames.AppInsightsInstrumentationKey] = ApplicationInsightsKey
                });
            });

            HttpClient = TestHost.HttpClient;

            TestHelpers.WaitForWebHost(HttpClient);
        }
        public async Task HostStatusReturns_IfHostJsonError()
        {
            string hostJsonPath = Path.Combine(_hostPath, ScriptConstants.HostMetadataFileName);

            // Simulate a non-empty file without a 'version'
            JObject hostConfig = JObject.FromObject(new
            {
                functionTimeout = TimeSpan.FromSeconds(30)
            });

            await File.WriteAllTextAsync(hostJsonPath, hostConfig.ToString());

            string logPath = Path.Combine(Path.GetTempPath(), @"Functions");

            _host = new TestFunctionHost(_hostPath, logPath, _ => { });

            // Ping the status endpoint to ensure we see the exception
            HostStatus status = await _host.GetHostStatusAsync();

            Assert.Equal("Error", status.State);
            Assert.Equal("Microsoft.Azure.WebJobs.Script: The host.json file is missing the required 'version' property. See https://aka.ms/functions-hostjson for steps to migrate the configuration file.", status.Errors.Single());

            // Due to https://github.com/Azure/azure-functions-host/issues/1351, slow this down to ensure
            // we have a host running and watching for file changes.
            await TestHelpers.Await(() =>
            {
                return(_host.GetLog().Contains("[Microsoft.Extensions.Hosting.Internal.Host] Hosting started"));
            });

            // Now update the file and make sure it auto-restarts.
            hostConfig["version"] = "2.0";
            await File.WriteAllTextAsync(hostJsonPath, hostConfig.ToString());

            await TestHelpers.Await(async() =>
            {
                status = await _host.GetHostStatusAsync();
                return(status.State == $"{ScriptHostState.Running}");
            }, userMessageCallback : _host.GetLog);

            Assert.Null(status.Errors);
        }
示例#12
0
        public async Task FileLogger_IOExceptionDuringInvocation_Recovers()
        {
            var fileWriterFactory = new TestFileWriterFactory(onAppendLine: null,
                                                              onFlush: () =>
            {
                // The below function will fail, causing an immediate flush. This exception
                // simulates the disk being full. ExecutionEvents should be logged as expected
                // and the "Finished" event should get logged.
                throw new IOException();
            });

            using (var host = new TestFunctionHost(_scriptRoot, _testLogPath,
                                                   configureWebHostServices: s =>
            {
                s.AddSingleton <IEventGenerator>(_ => _eventGenerator);
            },
                                                   configureScriptHostServices: s =>
            {
                s.AddSingleton <IFileWriterFactory>(_ => fileWriterFactory);

                s.PostConfigure <ScriptJobHostOptions>(o =>
                {
                    o.FileLoggingMode = FileLoggingMode.Always;
                    o.Functions = new[] { "HttpTrigger-Scenarios" };
                });
            }))
            {
                // Issue an invalid request that fails.
                var content  = new StringContent(JsonConvert.SerializeObject(new { scenario = "invalid" }));
                var response = await host.HttpClient.PostAsync("/api/HttpTrigger-Scenarios", content);

                Assert.Equal(HttpStatusCode.InternalServerError, response.StatusCode);

                await TestHelpers.Await(() =>
                {
                    var executionEvents = _eventGenerator.GetFunctionExecutionEvents();
                    return(executionEvents.SingleOrDefault(p => p.ExecutionStage == ExecutionStage.Finished) != null);
                });
            }
        }
示例#13
0
        private TestFunctionHost StartLocalHost(string baseTestPath, string sourceFunctionApp, string[] allowedList, IList <IFunctionProvider> providers, IEnvironment testEnvironment)
        {
            string appContent  = Path.Combine(baseTestPath, "FunctionApp");
            string testLogPath = Path.Combine(baseTestPath, "Logs");

            var syncTriggerMock = new Mock <IFunctionsSyncManager>(MockBehavior.Strict);

            syncTriggerMock.Setup(p => p.TrySyncTriggersAsync(It.IsAny <bool>())).ReturnsAsync(new SyncTriggersResult {
                Success = true
            });

            FileUtility.CopyDirectory(sourceFunctionApp, appContent);
            var host = new TestFunctionHost(sourceFunctionApp, testLogPath,
                                            configureScriptHostWebJobsBuilder: builder =>
            {
                foreach (var provider in providers)
                {
                    builder.Services.AddSingleton(provider);
                }

                if (allowedList != null && allowedList.Length != 0)
                {
                    builder.Services.Configure <ScriptJobHostOptions>(o =>
                    {
                        o.Functions = allowedList;
                    });
                }

                builder.Services.AddSingleton(testEnvironment);
            },
                                            configureScriptHostServices: s =>
            {
                s.AddSingleton(syncTriggerMock.Object);
            });

            return(host);
        }
示例#14
0
        public async Task DisposedResolver_UsesFullStackTrace()
        {
            var host = new TestFunctionHost(@"TestScripts\CSharp",
                                            configureScriptHostServices: s =>
            {
                s.AddSingleton <IExtensionConfigProvider, CustomTriggerExtensionConfigProvider>();
                s.Configure <ScriptJobHostOptions>(o => o.Functions = new[] { "CustomTrigger" });
                s.AddSingleton <ILoggerFactory, TestScriptLoggerFactory>();
            });

            await CustomListener.RunAsync("one");

            host.Dispose();

            // In this scenario, the function is considered failed even though the function itself was never called.
            var result = await CustomListener.RunAsync("two");

            Assert.False(result.Succeeded);

            var ex = result.Exception;

            Assert.Equal($"The host is disposed and cannot be used. Disposed object: '{typeof(ScopedResolver).FullName}'; Found IListener in stack trace: '{typeof(CustomListener).AssemblyQualifiedName}'", ex.Message);
            Assert.Contains("CustomListener.RunAsync", ex.StackTrace);
        }