public async Task EmptyHost_StartsSuccessfully() { string functionDir = Path.Combine(TestHelpers.FunctionsTestDirectory, "Functions", Guid.NewGuid().ToString()); Directory.CreateDirectory(functionDir); // important for the repro that this directory does not exist string logDir = Path.Combine(TestHelpers.FunctionsTestDirectory, "Logs", Guid.NewGuid().ToString()); JObject hostConfig = new JObject { { "id", "123456" } }; File.WriteAllText(Path.Combine(functionDir, ScriptConstants.HostMetadataFileName), hostConfig.ToString()); ScriptHostConfiguration config = new ScriptHostConfiguration { RootScriptPath = functionDir, RootLogPath = logDir, FileLoggingMode = FileLoggingMode.Always }; var eventManagerMock = new Mock <IScriptEventManager>(); ScriptHostManager hostManager = new ScriptHostManager(config, eventManagerMock.Object); // start the host and wait for it to be running Task runTask = Task.Run(() => hostManager.RunAndBlock()); await TestHelpers.Await(() => hostManager.State == ScriptHostState.Running, timeout : 10000); // exercise restart hostManager.RestartHost(); Assert.Equal(ScriptHostState.Default, hostManager.State); await TestHelpers.Await(() => hostManager.State == ScriptHostState.Running, timeout : 10000); // stop the host fully hostManager.Stop(); Assert.Equal(ScriptHostState.Default, hostManager.State); await Task.Delay(FileTraceWriter.LogFlushIntervalMs); string hostLogFilePath = Directory.EnumerateFiles(Path.Combine(logDir, "Host")).Single(); string hostLogs = File.ReadAllText(hostLogFilePath); Assert.Contains("Generating 0 job function(s)", hostLogs); Assert.Contains("No job functions found.", hostLogs); Assert.Contains("Job host started", hostLogs); Assert.Contains("Job host stopped", hostLogs); }
public async Task RunAndBlock_ParseError_LogsError() { TestLoggerProvider loggerProvider = new TestLoggerProvider(); TestLoggerProviderFactory factory = new TestLoggerProviderFactory(loggerProvider, includeDefaultLoggerProviders: false); string rootPath = Path.Combine(Environment.CurrentDirectory, "ScriptHostTests"); if (!Directory.Exists(rootPath)) { Directory.CreateDirectory(rootPath); } var configPath = Path.Combine(rootPath, "host.json"); File.WriteAllText(configPath, @"{<unparseable>}"); var config = new ScriptHostConfiguration() { RootScriptPath = rootPath }; config.HostConfig.HostId = ID; var scriptHostFactory = new TestScriptHostFactory(); var eventManagerMock = new Mock <IScriptEventManager>(); var hostManager = new ScriptHostManager(config, _settingsManager, scriptHostFactory, eventManagerMock.Object, loggerProviderFactory: factory); Task taskIgnore = Task.Run(() => hostManager.RunAndBlock()); await TestHelpers.Await(() => hostManager.State == ScriptHostState.Error, 3000, 50); Assert.Equal(ScriptHostState.Error, hostManager.State); hostManager.Stop(); var ex = hostManager.LastError; Assert.True(ex is FormatException); var expectedMessage = $"Unable to parse host configuration file '{configPath}'."; Assert.Equal(expectedMessage, ex.Message); var logger = loggerProvider.CreatedLoggers.Last(); var logMessage = logger.GetLogMessages()[0]; Assert.StartsWith("A ScriptHost error has occurred", logMessage.FormattedMessage); Assert.Equal(expectedMessage, logMessage.Exception.Message); }
public async Task UpdateFileAndRestart() { Random r = new Random(); CancellationTokenSource cts = new CancellationTokenSource(); var fixture = new NodeEndToEndTests.TestFixture(); var blob1 = UpdateOutputName("testblob", "first", fixture); await fixture.Host.StopAsync(); var config = fixture.Host.ScriptConfig; using (var manager = new ScriptHostManager(config)) { // Background task to run while the main thread is pumping events at RunAndBlock(). Thread t = new Thread(_ => { // Wait for initial execution. TestHelpers.Await(() => { return(blob1.Exists()); }, timeout: 10 * 1000).Wait(); // This changes the bindings so that we now write to blob2 var blob2 = UpdateOutputName("first", "second", fixture); // wait for newly executed TestHelpers.Await(() => { return(blob2.Exists()); }, timeout: 10 * 1000).Wait(); manager.Stop(); }); t.Start(); manager.RunAndBlock(cts.Token); t.Join(); } }
public async Task EmptyHost_StartsSuccessfully() { string functionDir = Path.Combine(TestHelpers.FunctionsTestDirectory, "Functions", Guid.NewGuid().ToString()); Directory.CreateDirectory(functionDir); // important for the repro that this directory does not exist string logDir = Path.Combine(TestHelpers.FunctionsTestDirectory, "Logs", Guid.NewGuid().ToString()); JObject hostConfig = new JObject { { "id", "123456" } }; File.WriteAllText(Path.Combine(functionDir, ScriptConstants.HostMetadataFileName), hostConfig.ToString()); ScriptHostConfiguration config = new ScriptHostConfiguration { RootScriptPath = functionDir, RootLogPath = logDir, FileLoggingEnabled = true }; ScriptHostManager hostManager = new ScriptHostManager(config); Task runTask = Task.Run(() => hostManager.RunAndBlock()); await TestHelpers.Await(() => hostManager.IsRunning, timeout : 10000); hostManager.Stop(); Assert.False(hostManager.IsRunning); string hostLogFilePath = Directory.EnumerateFiles(Path.Combine(logDir, "Host")).Single(); string hostLogs = File.ReadAllText(hostLogFilePath); Assert.True(hostLogs.Contains("Generating 0 job function(s)")); Assert.True(hostLogs.Contains("No job functions found.")); Assert.True(hostLogs.Contains("Job host started")); Assert.True(hostLogs.Contains("Job host stopped")); }
public async Task RunAndBlock_HostJsonValueError_LogsError() { // Try to load valid host.json file that has an out-of-range value. // Ensure that it's logged to ILogger string rootPath = Path.Combine(Environment.CurrentDirectory, @"TestScripts\OutOfRange"); ScriptHostConfiguration config = new ScriptHostConfiguration() { RootScriptPath = rootPath }; TestLoggerProvider provider = new TestLoggerProvider(); var loggerProviderFactory = new TestLoggerProviderFactory(provider, includeDefaultLoggerProviders: false); var factoryMock = new Mock <IScriptHostFactory>(); var scriptHostFactory = new TestScriptHostFactory(); var eventManagerMock = new Mock <IScriptEventManager>(); var hostManager = new ScriptHostManager(config, _settingsManager, scriptHostFactory, eventManagerMock.Object, loggerProviderFactory: loggerProviderFactory); Task taskIgnore = Task.Run(() => hostManager.RunAndBlock()); await TestHelpers.Await(() => hostManager.State == ScriptHostState.Error, 3000, 50); Assert.Equal(ScriptHostState.Error, hostManager.State); Assert.False(hostManager.CanInvoke()); hostManager.Stop(); var ex = hostManager.LastError; Assert.True(ex is ArgumentOutOfRangeException); string msg = "A ScriptHost error has occurred"; var startupLogger = provider.CreatedLoggers.Last(); var loggerMessage = startupLogger.GetLogMessages().First(); Assert.Equal(msg, loggerMessage.FormattedMessage); Assert.Same(ex, loggerMessage.Exception); }
public async Task UpdateFileAndRestart() { var fixture = new NodeScriptHostTests.TestFixture(false); var config = fixture.Host.ScriptConfig; config.OnConfigurationApplied = c => { c.Functions = new Collection <string> { "TimerTrigger" }; }; var blob1 = await UpdateOutputName("testblob", "first", fixture); using (var eventManager = new ScriptEventManager()) using (var manager = new ScriptHostManager(config, eventManager)) { string GetErrorTraces() { var messages = fixture.LoggerProvider.GetAllLogMessages() .Where(t => t.Level == LogLevel.Error) .Select(t => t.FormattedMessage); return(string.Join(Environment.NewLine, messages)); } List <Exception> exceptions = new List <Exception>(); // Background task to run while the main thread is pumping events at RunAndBlock(). Thread backgroundThread = new Thread(_ => { try { // don't start until the manager is running TestHelpers.Await(() => manager.State == ScriptHostState.Running, userMessageCallback: GetErrorTraces).GetAwaiter().GetResult(); // Wait for initial execution. TestHelpers.Await(async() => { bool exists = await blob1.ExistsAsync(); return(exists); }, timeout: 10 * 1000, userMessageCallback: GetErrorTraces).GetAwaiter().GetResult(); // This changes the bindings so that we now write to blob2 var blob2 = UpdateOutputName("first", "testblob", fixture).Result; // wait for newly executed TestHelpers.Await(async() => { bool exists = await blob2.ExistsAsync(); return(exists); }, timeout: 30 * 1000, userMessageCallback: GetErrorTraces).GetAwaiter().GetResult(); // The TimerTrigger can fire before the host is fully started. To be more // reliably clean up the test, wait until it is running before calling Stop. TestHelpers.Await(() => manager.State == ScriptHostState.Running, userMessageCallback: GetErrorTraces).GetAwaiter().GetResult(); } catch (Exception ex) { exceptions.Add(ex); } finally { try { // Calling Stop (rather than using a token) lets us wait until all listeners have stopped. manager.Stop(); } catch (Exception ex) { exceptions.Add(ex); } } }); try { backgroundThread.Start(); manager.RunAndBlock(); Assert.True(backgroundThread.Join(60000), "The background task did not complete in 60 seconds."); string exceptionString = string.Join(Environment.NewLine, exceptions.Select(p => p.ToString())); Assert.True(exceptions.Count() == 0, exceptionString); } finally { // make sure to put the original names back await UpdateOutputName("first", "testblob", fixture); } } }
public async Task RenameFunctionAndRestart() { var oldDirectory = Path.Combine(Directory.GetCurrentDirectory(), "TestScripts/Node/TimerTrigger"); var newDirectory = Path.Combine(Directory.GetCurrentDirectory(), "TestScripts/Node/MovedTrigger"); var fixture = new NodeScriptHostTests.TestFixture(false); var config = fixture.Host.ScriptConfig; config.OnConfigurationApplied = c => { c.Functions = new Collection <string> { "TimerTrigger", "MovedTrigger" }; }; var blob = fixture.TestOutputContainer.GetBlockBlobReference("testblob"); await blob.DeleteIfExistsAsync(); var mockEnvironment = new Mock <IScriptHostEnvironment>(); using (var eventManager = new ScriptEventManager()) using (var manager = new ScriptHostManager(config, eventManager, mockEnvironment.Object)) using (var resetEvent = new ManualResetEventSlim()) { List <Exception> exceptions = new List <Exception>(); mockEnvironment.Setup(e => e.RestartHost()) .Callback(() => { resetEvent.Set(); manager.RestartHost(); }); // Background task to run while the main thread is pumping events at RunAndBlock(). Thread backgroundThread = new Thread(_ => { try { // don't start until the manager is running TestHelpers.Await(() => manager.State == ScriptHostState.Running, userMessageCallback: () => "Host did not start in time.").GetAwaiter().GetResult(); // Wait for initial execution. TestHelpers.Await(async() => { bool exists = await blob.ExistsAsync(); return(exists); }, timeout: 10 * 1000, userMessageCallback: () => $"Blob '{blob.Uri}' was not created by 'TimerTrigger' in time.").GetAwaiter().GetResult(); // find __dirname from blob string text; using (var stream = new MemoryStream()) { blob.DownloadToStreamAsync(stream).Wait(); text = System.Text.Encoding.UTF8.GetString(stream.ToArray()); } Assert.Contains("TimerTrigger", text); // rename directory & delete old blob Directory.Move(oldDirectory, newDirectory); resetEvent.Wait(TimeSpan.FromSeconds(10)); blob.DeleteIfExistsAsync().GetAwaiter().GetResult(); // wait for newly executed TestHelpers.Await(async() => { bool exists = await blob.ExistsAsync(); return(exists); }, timeout: 30 * 1000, userMessageCallback: () => $"Blob '{blob.Uri}' was not created by 'MovedTrigger' in time.").GetAwaiter().GetResult(); using (var stream = new MemoryStream()) { blob.DownloadToStreamAsync(stream).Wait(); text = System.Text.Encoding.UTF8.GetString(stream.ToArray()); } Assert.Contains("MovedTrigger", text); // The TimerTrigger can fire before the host is fully started. To be more // reliably clean up the test, wait until it is running before calling Stop. TestHelpers.Await(() => manager.State == ScriptHostState.Running).GetAwaiter().GetResult(); } catch (Exception ex) { exceptions.Add(ex); } finally { try { manager.Stop(); } catch (Exception ex) { exceptions.Add(ex); } } }); try { backgroundThread.Start(); manager.RunAndBlock(); Assert.True(backgroundThread.Join(60000), "The background task did not complete in 60 seconds."); string exceptionString = string.Join(Environment.NewLine, exceptions.Select(p => p.ToString())); Assert.True(exceptions.Count() == 0, exceptionString); } finally { // Move the directory back after the host has stopped to prevent // unnecessary host restarts if (Directory.Exists(newDirectory)) { Directory.Move(newDirectory, oldDirectory); } } } }
public async Task EmptyHost_StartsSuccessfully() { string functionDir = Path.Combine(TestHelpers.FunctionsTestDirectory, "Functions", Guid.NewGuid().ToString()); Directory.CreateDirectory(functionDir); // important for the repro that this directory does not exist string logDir = Path.Combine(TestHelpers.FunctionsTestDirectory, "Logs", Guid.NewGuid().ToString()); JObject hostConfig = new JObject { { "id", "123456" } }; File.WriteAllText(Path.Combine(functionDir, ScriptConstants.HostMetadataFileName), hostConfig.ToString()); ScriptHostConfiguration config = new ScriptHostConfiguration { RootScriptPath = functionDir, RootLogPath = logDir, FileLoggingMode = FileLoggingMode.Always }; ScriptHostManager hostManager = new ScriptHostManager(config); Task runTask = Task.Run(() => hostManager.RunAndBlock()); await TestHelpers.Await(() => hostManager.State == ScriptHostState.Running, timeout: 10000); hostManager.Stop(); Assert.Equal(ScriptHostState.Default, hostManager.State); await Task.Delay(FileTraceWriter.LogFlushIntervalMs); string hostLogFilePath = Directory.EnumerateFiles(Path.Combine(logDir, "Host")).Single(); string hostLogs = File.ReadAllText(hostLogFilePath); Assert.Contains("Generating 0 job function(s)", hostLogs); Assert.Contains("No job functions found.", hostLogs); Assert.Contains("Job host started", hostLogs); Assert.Contains("Job host stopped", hostLogs); }