protected EndToEndTestFixture(string rootPath) { string connectionString = AmbientConnectionStringProvider.Instance.GetConnectionString(ConnectionStringNames.Storage); CloudStorageAccount storageAccount = CloudStorageAccount.Parse(connectionString); _queueClient = storageAccount.CreateCloudQueueClient(); _blobClient = storageAccount.CreateCloudBlobClient(); CreateTestStorageEntities(); TraceWriter = new TestTraceWriter(TraceLevel.Verbose); ScriptHostConfiguration config = new ScriptHostConfiguration() { RootScriptPath = rootPath, TraceWriter = TraceWriter, FileLoggingEnabled = true }; HostManager = new ScriptHostManager(config); Thread t = new Thread(_ => { HostManager.RunAndBlock(); }); t.Start(); TestHelpers.Await(() => HostManager.IsRunning).Wait(); }
protected EndToEndTestFixture(string rootPath, string testId) { _settingsManager = ScriptSettingsManager.Instance; FixtureId = testId; string connectionString = AmbientConnectionStringProvider.Instance.GetConnectionString(ConnectionStringNames.Storage); CloudStorageAccount storageAccount = CloudStorageAccount.Parse(connectionString); QueueClient = storageAccount.CreateCloudQueueClient(); BlobClient = storageAccount.CreateCloudBlobClient(); TableClient = storageAccount.CreateCloudTableClient(); CreateTestStorageEntities(); TraceWriter = new TestTraceWriter(TraceLevel.Verbose); ApiHubTestHelper.SetDefaultConnectionFactory(); ScriptHostConfiguration config = new ScriptHostConfiguration() { RootScriptPath = rootPath, TraceWriter = TraceWriter, FileLoggingMode = FileLoggingMode.Always }; RequestConfiguration = new HttpConfiguration(); RequestConfiguration.Formatters.Add(new PlaintextMediaTypeFormatter()); // Reset the timer logs first, since one of the tests will // be checking them TestHelpers.ClearFunctionLogs("TimerTrigger"); Host = ScriptHost.Create(_settingsManager, config); Host.Start(); }
public void Flush_FlushesInternalWriters() { var traceWriter = new TestTraceWriter(TraceLevel.Error); var compositeWriter = new CompositeTraceWriter(new[] { traceWriter }); string message = "Test trace"; compositeWriter.Error(message); compositeWriter.Flush(); Assert.True(traceWriter.Flushed); }
public void Trace_CallsInnerWriterTrace() { var traceWriter = new TestTraceWriter(TraceLevel.Verbose); var compositeWriter = new CompositeTraceWriter(new[] { traceWriter }); string message = "Test trace"; compositeWriter.Verbose(message); TraceEvent trace = traceWriter.Traces.FirstOrDefault(); Assert.NotNull(trace); Assert.Equal(TraceLevel.Verbose, trace.Level); Assert.Equal(message, trace.Message); }
public void Trace_RespectsInnerWriterLevel() { var traceWriter = new TestTraceWriter(TraceLevel.Error); var compositeWriter = new CompositeTraceWriter(new[] { traceWriter }); string message = "Test trace"; compositeWriter.Verbose(message); compositeWriter.Error(message); Assert.Equal(1, traceWriter.Traces.Count); TraceEvent trace = traceWriter.Traces.First(); Assert.Equal(TraceLevel.Error, trace.Level); Assert.Equal(message, trace.Message); }
public async Task HasLease_WhenLeaseIsAcquired_ReturnsTrue() { string connectionString = AmbientConnectionStringProvider.Instance.GetConnectionString(ConnectionStringNames.Storage); string hostId = Guid.NewGuid().ToString(); string instanceId = Guid.NewGuid().ToString(); var traceWriter = new TestTraceWriter(System.Diagnostics.TraceLevel.Verbose); using (var manager = await BlobLeaseManager.CreateAsync(connectionString, TimeSpan.FromSeconds(15), hostId, instanceId, traceWriter)) { await TestHelpers.Await(() => manager.HasLease); Assert.Equal(instanceId, manager.LeaseId); } await ClearLeaseBlob(hostId); }
public void ResolveAssembly_WithIndirectPrivateDependency_IsResolved() { var resolver = new FunctionAssemblyLoader("c:\\"); var metadata1 = new FunctionMetadata { Name = "Test1", ScriptFile = @"c:\testroot\test1\test.tst" }; var metadata2 = new FunctionMetadata { Name = "Test2", ScriptFile = @"c:\testroot\test2\test.tst" }; var traceWriter = new TestTraceWriter(TraceLevel.Verbose); var mockResolver = new Mock<IFunctionMetadataResolver>(); mockResolver.Setup(m => m.ResolveAssembly("MyTestAssembly.dll")) .Returns(new TestAssembly(new AssemblyName("MyTestAssembly"))); resolver.CreateOrUpdateContext(metadata1, this.GetType().Assembly, new FunctionMetadataResolver(metadata1, new Collection<ScriptBindingProvider>(), traceWriter), traceWriter); resolver.CreateOrUpdateContext(metadata2, this.GetType().Assembly, mockResolver.Object, traceWriter); Assembly result = resolver.ResolveAssembly(null, new System.ResolveEventArgs("MyTestAssembly.dll", new TestAssembly(new AssemblyName("MyDirectReference"), @"file:///c:/testroot/test2/bin/MyDirectReference.dll"))); Assert.NotNull(result); }
public async Task HasLeaseChanged_WhenLeaseIsLostAndStateChanges_IsFired() { string hostId = Guid.NewGuid().ToString(); string instanceId = Guid.NewGuid().ToString(); string connectionString = AmbientConnectionStringProvider.Instance.GetConnectionString(ConnectionStringNames.Storage); ICloudBlob blob = await GetLockBlobAsync(connectionString, hostId); var traceWriter = new TestTraceWriter(TraceLevel.Verbose); var resetEvent = new ManualResetEventSlim(); BlobLeaseManager manager = null; string tempLeaseId = null; using (manager = new BlobLeaseManager(blob, TimeSpan.FromSeconds(15), hostId, instanceId, traceWriter, TimeSpan.FromSeconds(3))) { try { await TestHelpers.Await(() => manager.HasLease); manager.HasLeaseChanged += (s, a) => resetEvent.Set(); // Release the manager's lease and acquire one with a different id await blob.ReleaseLeaseAsync(new AccessCondition { LeaseId = manager.LeaseId }); tempLeaseId = await blob.AcquireLeaseAsync(TimeSpan.FromSeconds(30), Guid.NewGuid().ToString()); } finally { if (tempLeaseId != null) { await blob.ReleaseLeaseAsync(new AccessCondition { LeaseId = tempLeaseId }); } } resetEvent.Wait(TimeSpan.FromSeconds(15)); } Assert.True(resetEvent.IsSet); Assert.False(manager.HasLease, $"{nameof(BlobLeaseManager.HasLease)} was not correctly set to 'false' when lease lost."); await ClearLeaseBlob(hostId); }
public void PropertiesAreProperlySet() { var traceWriter = new TestTraceWriter(TraceLevel.Verbose); var properties = new Dictionary<string, object> { { "prop1", "prop1" }, { "prop2", "prop2" }, { "prop3", "prop3" } }; traceWriter.Trace("test", TraceLevel.Verbose, properties); Assert.Equal(1, traceWriter.Traces.Count); var trace = traceWriter.Traces.First(); foreach (var property in properties) { Assert.True(trace.Properties.ContainsKey(property.Key)); Assert.Equal(property.Value, trace.Properties[property.Key]); } }
public void Apply_CreatesInterceptingTraceWriter() { TestTraceWriter traceWriter = new TestTraceWriter(TraceLevel.Verbose); traceWriter.Info("Test message"); var traceEvent = traceWriter.Traces.Single(); Assert.Equal(0, traceEvent.Properties.Count); traceWriter.Traces.Clear(); var properties = new Dictionary<string, object> { { "Foo", 123 }, { "Bar", 456 } }; var interceptingTraceWriter = traceWriter.Apply(properties); interceptingTraceWriter.Info("Test message"); traceEvent = traceWriter.Traces.Single(); Assert.Equal(2, traceEvent.Properties.Count); Assert.Equal(123, traceEvent.Properties["Foo"]); Assert.Equal(456, traceEvent.Properties["Bar"]); }
public async Task HasLeaseChanged_WhenLeaseIsAcquiredAndStateChanges_IsFired() { string hostId = Guid.NewGuid().ToString(); string instanceId = Guid.NewGuid().ToString(); var traceWriter = new TestTraceWriter(TraceLevel.Verbose); var resetEvent = new ManualResetEventSlim(); string connectionString = AmbientConnectionStringProvider.Instance.GetConnectionString(ConnectionStringNames.Storage); ICloudBlob blob = await GetLockBlobAsync(connectionString, hostId); // Acquire a lease on the host lock blob string leaseId = await blob.AcquireLeaseAsync(TimeSpan.FromSeconds(15)); BlobLeaseManager manager = null; try { manager = await BlobLeaseManager.CreateAsync(connectionString, TimeSpan.FromSeconds(15), hostId, instanceId, traceWriter); manager.HasLeaseChanged += (s, a) => resetEvent.Set(); } finally { await blob.ReleaseLeaseAsync(new AccessCondition { LeaseId = leaseId }); } resetEvent.Wait(TimeSpan.FromSeconds(15)); bool hasLease = manager.HasLease; string actualLeaseId = manager.LeaseId; manager.Dispose(); Assert.True(resetEvent.IsSet); Assert.True(hasLease, $"{nameof(BlobLeaseManager.HasLease)} was not correctly set to 'true' when lease was acquired."); Assert.Equal(instanceId, actualLeaseId); await ClearLeaseBlob(hostId); }
public async Task TraceOutputsMessagesWhenLeaseRenewalFails() { string hostId = Guid.NewGuid().ToString(); string instanceId = Guid.NewGuid().ToString(); var traceWriter = new TestTraceWriter(TraceLevel.Verbose); var renewResetEvent = new ManualResetEventSlim(); var blobMock = new Mock <ICloudBlob>(); blobMock.Setup(b => b.AcquireLeaseAsync(It.IsAny <TimeSpan>(), It.IsAny <string>())) .Returns(() => Task.FromResult(hostId)); blobMock.Setup(b => b.RenewLeaseAsync(It.IsAny <AccessCondition>())) .Returns(() => Task.FromException(new StorageException(new RequestResult { HttpStatusCode = 409 }, "test", null))) .Callback(() => renewResetEvent.Set()); using (var manager = new BlobLeaseManager(blobMock.Object, TimeSpan.FromSeconds(5), hostId, instanceId, traceWriter)) { renewResetEvent.Wait(TimeSpan.FromSeconds(10)); // Make sure we have enough time to trace the renewal await TestHelpers.Await(() => traceWriter.Traces.Count == 2, 5000, 500); } TraceEvent acquisitionEvent = traceWriter.Traces.First(); Assert.Contains($"Host lock lease acquired by instance ID '{instanceId}'.", acquisitionEvent.Message); Assert.Equal(TraceLevel.Info, acquisitionEvent.Level); TraceEvent renewalEvent = traceWriter.Traces.Skip(1).First(); string pattern = @"Failed to renew host lock lease: Another host has acquired the lease. The last successful renewal completed at (.+) \([0-9]+ milliseconds ago\) with a duration of [0-9]+ milliseconds."; Assert.True(Regex.IsMatch(renewalEvent.Message, pattern), $"Expected trace event {pattern} not found."); Assert.Equal(TraceLevel.Info, renewalEvent.Level); }
public async Task DifferentHosts_UsingSameStorageAccount_CanObtainLease() { string hostId1 = Guid.NewGuid().ToString(); string hostId2 = Guid.NewGuid().ToString(); string instanceId = Guid.NewGuid().ToString(); string connectionString = AmbientConnectionStringProvider.Instance.GetConnectionString(ConnectionStringNames.Storage); var traceWriter = new TestTraceWriter(TraceLevel.Verbose); using (var manager1 = await BlobLeaseManager.CreateAsync(connectionString, TimeSpan.FromSeconds(15), hostId1, instanceId, traceWriter)) using (var manager2 = await BlobLeaseManager.CreateAsync(connectionString, TimeSpan.FromSeconds(15), hostId2, instanceId, traceWriter)) { Task manager1Check = TestHelpers.Await(() => manager1.HasLease); Task manager2Check = TestHelpers.Await(() => manager2.HasLease); await Task.WhenAll(manager1Check, manager2Check); } await Task.WhenAll(ClearLeaseBlob(hostId1), ClearLeaseBlob(hostId2)); }
public async Task TraceOutputsMessagesWhenLeaseRenewalFails() { string hostId = Guid.NewGuid().ToString(); string instanceId = Guid.NewGuid().ToString(); var traceWriter = new TestTraceWriter(TraceLevel.Verbose); var renewResetEvent = new ManualResetEventSlim(); var blobMock = new Mock<ICloudBlob>(); blobMock.Setup(b => b.AcquireLeaseAsync(It.IsAny<TimeSpan>(), It.IsAny<string>())) .Returns(() => Task.FromResult(hostId)); blobMock.Setup(b => b.RenewLeaseAsync(It.IsAny<AccessCondition>())) .Returns(() => Task.FromException(new StorageException(new RequestResult { HttpStatusCode = 409 }, "test", null))) .Callback(() => renewResetEvent.Set()); using (var manager = new BlobLeaseManager(blobMock.Object, TimeSpan.FromSeconds(5), hostId, instanceId, traceWriter)) { renewResetEvent.Wait(TimeSpan.FromSeconds(10)); // Make sure we have enough time to trace the renewal await TestHelpers.Await(() => traceWriter.Traces.Count == 2, 5000, 500); } TraceEvent acquisitionEvent = traceWriter.Traces.First(); Assert.Contains($"Host lock lease acquired by instance ID '{instanceId}'.", acquisitionEvent.Message); Assert.Equal(TraceLevel.Info, acquisitionEvent.Level); TraceEvent renewalEvent = traceWriter.Traces.Skip(1).First(); string pattern = @"Failed to renew host lock lease: Another host has acquired the lease. The last successful renewal completed at (.+) \([0-9]+ milliseconds ago\) with a duration of [0-9]+ milliseconds."; Assert.True(Regex.IsMatch(renewalEvent.Message, pattern), $"Expected trace event {pattern} not found."); Assert.Equal(TraceLevel.Info, renewalEvent.Level); }
public async Task TraceOutputsMessagesWhenLeaseIsAcquired() { string hostId = Guid.NewGuid().ToString(); string instanceId = Guid.NewGuid().ToString(); var traceWriter = new TestTraceWriter(TraceLevel.Verbose); var renewResetEvent = new ManualResetEventSlim(); var blobMock = new Mock<ICloudBlob>(); blobMock.Setup(b => b.AcquireLeaseAsync(It.IsAny<TimeSpan>(), It.IsAny<string>())) .Returns(() => Task.FromResult(hostId)); using (var manager = new BlobLeaseManager(blobMock.Object, TimeSpan.FromSeconds(5), hostId, instanceId, traceWriter)) { renewResetEvent.Wait(TimeSpan.FromSeconds(10)); // Make sure we have enough time to trace the renewal await TestHelpers.Await(() => traceWriter.Traces.Count == 1, 5000, 500); } TraceEvent acquisitionEvent = traceWriter.Traces.First(); Assert.Contains($"Host lock lease acquired by instance ID '{instanceId}'.", acquisitionEvent.Message); Assert.Equal(TraceLevel.Info, acquisitionEvent.Level); }
internal async Task GeneratedMethods_WithOutParams_DoNotCauseDeadlocks(string fixture) { var traceWriter = new TestTraceWriter(TraceLevel.Verbose); ScriptHostConfiguration config = new ScriptHostConfiguration() { RootScriptPath = @"TestScripts\FunctionGeneration", TraceWriter = traceWriter }; string secretsPath = Path.Combine(Path.GetTempPath(), @"FunctionTests\Secrets"); WebHostSettings webHostSettings = new WebHostSettings(); var secretManager = new SecretManager(SettingsManager, secretsPath, NullTraceWriter.Instance); using (var manager = new WebScriptHostManager(config, new TestSecretManagerFactory(secretManager), SettingsManager, webHostSettings)) { Thread runLoopThread = new Thread(_ => { manager.RunAndBlock(CancellationToken.None); }); runLoopThread.IsBackground = true; runLoopThread.Start(); await TestHelpers.Await(() => { return manager.State == ScriptHostState.Running; }); var request = new HttpRequestMessage(HttpMethod.Get, String.Format("http://localhost/api/httptrigger-{0}", fixture)); FunctionDescriptor function = manager.GetHttpFunctionOrNull(request); SynchronizationContext currentContext = SynchronizationContext.Current; var resetEvent = new ManualResetEventSlim(); try { var requestThread = new Thread(() => { var context = new SingleThreadSynchronizationContext(); SynchronizationContext.SetSynchronizationContext(context); manager.HandleRequestAsync(function, request, CancellationToken.None) .ContinueWith(task => resetEvent.Set()); Thread.Sleep(500); context.Run(); }); requestThread.IsBackground = true; requestThread.Start(); bool threadSignaled = resetEvent.Wait(TimeSpan.FromSeconds(10)); requestThread.Abort(); Assert.True(threadSignaled, "Thread execution did not complete"); } finally { SynchronizationContext.SetSynchronizationContext(currentContext); manager.Stop(); } } }
public async Task Dispose_ReleasesBlobLease() { string hostId = Guid.NewGuid().ToString(); string instanceId = Guid.NewGuid().ToString(); string connectionString = AmbientConnectionStringProvider.Instance.GetConnectionString(ConnectionStringNames.Storage); var traceWriter = new TestTraceWriter(System.Diagnostics.TraceLevel.Verbose); using (var manager = await BlobLeaseManager.CreateAsync(connectionString, TimeSpan.FromSeconds(15), hostId, instanceId, traceWriter)) { await TestHelpers.Await(() => manager.HasLease); } ICloudBlob blob = await GetLockBlobAsync(connectionString, hostId); string leaseId = null; try { // Acquire a lease on the host lock blob leaseId = await blob.AcquireLeaseAsync(TimeSpan.FromSeconds(15)); await blob.ReleaseLeaseAsync(new AccessCondition { LeaseId = leaseId }); } catch (StorageException exc) when (exc.RequestInformation.HttpStatusCode == 409) { } Assert.False(string.IsNullOrEmpty(leaseId), "Failed to acquire a blob lease. The lease was not properly released."); await ClearLeaseBlob(hostId); }
public async Task Renew_WhenBlobIsDeleted_RecreatesBlob() { string hostId = Guid.NewGuid().ToString(); string instanceId = Guid.NewGuid().ToString(); var traceWriter = new TestTraceWriter(TraceLevel.Verbose); var renewResetEvent = new ManualResetEventSlim(); string connectionString = AmbientConnectionStringProvider.Instance.GetConnectionString(ConnectionStringNames.Storage); ICloudBlob blob = await GetLockBlobAsync(connectionString, hostId); var blobMock = new Mock<ICloudBlob>(); blobMock.Setup(b => b.AcquireLeaseAsync(It.IsAny<TimeSpan>(), It.IsAny<string>())) .Returns(() => Task.FromResult(hostId)); blobMock.Setup(b => b.RenewLeaseAsync(It.IsAny<AccessCondition>())) .Returns(() => Task.FromException<string>(new StorageException(new RequestResult { HttpStatusCode = 404 }, "test", null))) .Callback(() => Task.Delay(1000).ContinueWith(t => renewResetEvent.Set())); blobMock.SetupGet(b => b.ServiceClient).Returns(blob.ServiceClient); // Delete the blob await blob.DeleteIfExistsAsync(); using (var manager = new BlobLeaseManager(blobMock.Object, TimeSpan.FromSeconds(15), hostId, instanceId, traceWriter, TimeSpan.FromSeconds(3))) { renewResetEvent.Wait(TimeSpan.FromSeconds(10)); await TestHelpers.Await(() => manager.HasLease); } bool blobExists = await blob.ExistsAsync(); Assert.True(renewResetEvent.IsSet); Assert.True(blobExists); await ClearLeaseBlob(hostId); }
private void TestMessageAndLevel(Action<TraceWriter> traceHandler, string message, TraceLevel level) { var traceWriter = new TestTraceWriter(level); traceHandler(traceWriter); Assert.Equal(1, traceWriter.Traces.Count); Assert.Equal(level, traceWriter.Traces.First().Level); Assert.Equal(message, traceWriter.Traces.First().Message); }
public StandbyManagerTests() { _settingsManager = ScriptSettingsManager.Instance; _traceWriter = new TestTraceWriter(TraceLevel.Info); WebScriptHostManager.ResetStandbyMode(); }
internal async Task GeneratedMethods_WithOutParams_DoNotCauseDeadlocks(string fixture) { var traceWriter = new TestTraceWriter(TraceLevel.Verbose); ScriptHostConfiguration config = new ScriptHostConfiguration() { RootScriptPath = @"TestScripts\FunctionGeneration", TraceWriter = traceWriter }; string secretsPath = Path.Combine(Path.GetTempPath(), @"FunctionTests\Secrets"); ISecretsRepository repository = new FileSystemSecretsRepository(secretsPath); WebHostSettings webHostSettings = new WebHostSettings(); webHostSettings.SecretsPath = secretsPath; var secretManager = new SecretManager(SettingsManager, repository, NullTraceWriter.Instance); using (var manager = new WebScriptHostManager(config, new TestSecretManagerFactory(secretManager), SettingsManager, webHostSettings)) { Thread runLoopThread = new Thread(_ => { manager.RunAndBlock(CancellationToken.None); }); runLoopThread.IsBackground = true; runLoopThread.Start(); await TestHelpers.Await(() => { return(manager.State == ScriptHostState.Running); }); var request = new HttpRequestMessage(HttpMethod.Get, String.Format("http://localhost/api/httptrigger-{0}", fixture)); FunctionDescriptor function = manager.GetHttpFunctionOrNull(request); SynchronizationContext currentContext = SynchronizationContext.Current; var resetEvent = new ManualResetEventSlim(); try { var requestThread = new Thread(() => { var context = new SingleThreadSynchronizationContext(); SynchronizationContext.SetSynchronizationContext(context); manager.HandleRequestAsync(function, request, CancellationToken.None) .ContinueWith(task => resetEvent.Set()); Thread.Sleep(500); context.Run(); }); requestThread.IsBackground = true; requestThread.Start(); bool threadSignaled = resetEvent.Wait(TimeSpan.FromSeconds(10)); requestThread.Abort(); Assert.True(threadSignaled, "Thread execution did not complete"); } finally { SynchronizationContext.SetSynchronizationContext(currentContext); manager.Stop(); } } }
public async Task StandbyMode_EndToEnd() { var vars = new Dictionary <string, string> { { EnvironmentSettingNames.AzureWebsitePlaceholderMode, "1" }, { EnvironmentSettingNames.AzureWebsiteInstanceId, "87654639876900123453445678890144" } }; using (var env = new TestScopedEnvironmentVariable(vars)) { var httpConfig = new HttpConfiguration(); httpConfig.Formatters.Add(new PlaintextMediaTypeFormatter()); var settingsManager = ScriptSettingsManager.Instance; var testRootPath = Path.Combine(Path.GetTempPath(), "StandbyModeTest"); if (Directory.Exists(testRootPath)) { Directory.Delete(testRootPath, true); } var traceWriter = new TestTraceWriter(TraceLevel.Info); var webHostSettings = new WebHostSettings { IsSelfHost = true, LogPath = Path.Combine(testRootPath, "Logs"), SecretsPath = Path.Combine(testRootPath, "Secrets"), TraceWriter = traceWriter }; WebApiConfig.Register(httpConfig, _settingsManager, webHostSettings); var httpServer = new HttpServer(httpConfig); var httpClient = new HttpClient(httpServer); httpClient.BaseAddress = new Uri("https://localhost/"); TestHelpers.WaitForWebHost(httpClient); var traces = traceWriter.Traces.ToArray(); Assert.Equal($"Creating StandbyMode placeholder function directory ({Path.GetTempPath()}Functions\\Standby)", traces[0].Message); Assert.Equal("StandbyMode placeholder function directory created", traces[1].Message); // issue warmup request and verify var request = new HttpRequestMessage(HttpMethod.Get, "api/warmup"); var response = await httpClient.SendAsync(request); Assert.Equal(HttpStatusCode.OK, response.StatusCode); string responseBody = await response.Content.ReadAsStringAsync(); Assert.Equal("WarmUp complete.", responseBody); // issue warmup request with restart and verify request = new HttpRequestMessage(HttpMethod.Get, "api/warmup?restart=1"); response = await httpClient.SendAsync(request); Assert.Equal(HttpStatusCode.OK, response.StatusCode); responseBody = await response.Content.ReadAsStringAsync(); Assert.Equal("WarmUp complete.", responseBody); httpServer.Dispose(); httpClient.Dispose(); await Task.Delay(2000); // verify logs string[] logLines = traceWriter.Traces.Select(p => p.Message).ToArray(); Assert.Equal(2, logLines.Count(p => p.Contains("Host is in standby mode"))); Assert.Equal(1, logLines.Count(p => p.Contains("Stopping Host"))); Assert.Equal(2, logLines.Count(p => p.Contains("Executed 'Functions.WarmUp' (Succeeded"))); } }
public StandbyManagerTests() { _settingsManager = ScriptSettingsManager.Instance; _traceWriter = new TestTraceWriter(TraceLevel.Info); }
public void Restart_CreatesNew_FunctionTraceWriter() { string functionDir = @"TestScripts\CSharp"; var traceWriter = new TestTraceWriter(System.Diagnostics.TraceLevel.Info); ScriptHostConfiguration config = new ScriptHostConfiguration { RootScriptPath = functionDir, FileLoggingMode = FileLoggingMode.Always, TraceWriter = traceWriter }; string hostJsonPath = Path.Combine(functionDir, ScriptConstants.HostMetadataFileName); string originalHostJson = File.ReadAllText(hostJsonPath); // Only load two functions to start: JObject hostConfig = new JObject { { "id", "123456" }, { "functions", new JArray("ManualTrigger", "Scenarios") } }; File.WriteAllText(hostJsonPath, hostConfig.ToString()); CancellationTokenSource cts = new CancellationTokenSource(); ExceptionDispatchInfo exception = null; try { using (var manager = new ScriptHostManager(config)) { // Background task to run while the main thread is pumping events at RunAndBlock(). Thread t = new Thread(_ => { try { // don't start until the manager is running TestHelpers.Await(() => manager.State == ScriptHostState.Running).Wait(); var firstFileWriters = GetRemovableTraceWriters(manager.Instance); Assert.Equal(2, firstFileWriters.Count()); // update the host.json to only have one function hostConfig["functions"] = new JArray("ManualTrigger"); traceWriter.Traces.Clear(); File.WriteAllText(hostJsonPath, hostConfig.ToString()); TestHelpers.Await(() => traceWriter.Traces.Select(p => p.Message).Contains("Job host started")).Wait(); TestHelpers.Await(() => manager.State == ScriptHostState.Running).Wait(); var secondFileWriters = GetRemovableTraceWriters(manager.Instance); Assert.Equal(1, secondFileWriters.Count()); // make sure we have a new instance of the ManualTrigger writer and that it does // not throw an ObjectDisposedException when we use it Assert.DoesNotContain(secondFileWriters.Single(), firstFileWriters); secondFileWriters.Single().Info("test"); // add back the other function -- make sure the writer is not disposed hostConfig["functions"] = new JArray("ManualTrigger", "Scenarios"); traceWriter.Traces.Clear(); File.WriteAllText(hostJsonPath, hostConfig.ToString()); TestHelpers.Await(() => traceWriter.Traces.Select(p => p.Message).Contains("Job host started")).Wait(); TestHelpers.Await(() => manager.State == ScriptHostState.Running).Wait(); var thirdFileWriters = GetRemovableTraceWriters(manager.Instance); Assert.Equal(2, thirdFileWriters.Count()); // make sure these are all new and that they also do not throw var previousWriters = firstFileWriters.Concat(secondFileWriters); Assert.DoesNotContain(thirdFileWriters.First(), previousWriters); Assert.DoesNotContain(thirdFileWriters.Last(), previousWriters); thirdFileWriters.First().Info("test"); thirdFileWriters.Last().Info("test"); } catch (Exception ex) { exception = ExceptionDispatchInfo.Capture(ex); } finally { cts.Cancel(); } }); t.Start(); manager.RunAndBlock(cts.Token); t.Join(); } Assert.True(exception == null, exception?.SourceException?.ToString()); } finally { File.WriteAllText(hostJsonPath, originalHostJson); } }
private async Task RunTimeoutTest(string scriptLang, string functionName) { TestHelpers.ClearFunctionLogs(functionName); TimeSpan testTimeout = TimeSpan.FromSeconds(3); var traceWriter = new TestTraceWriter(TraceLevel.Verbose); using (var manager = await CreateAndStartScriptHostManager(scriptLang, functionName, testTimeout, traceWriter)) { string testData = Guid.NewGuid().ToString(); Dictionary<string, object> arguments = new Dictionary<string, object> { { "inputData", testData }, }; FunctionTimeoutException ex = await Assert.ThrowsAsync<FunctionTimeoutException>(() => manager.Instance.CallAsync(functionName, arguments)); await TestHelpers.Await(() => { // make sure logging from within the function worked // TODO: This doesn't appear to work for Powershell in AppVeyor. Need to investigate. //bool hasTestData = inProgressLogs.Any(l => l.Contains(testData)); var expectedMessage = $"Timeout value of {testTimeout} was exceeded by function: Functions.{functionName}"; var traces = string.Join(Environment.NewLine, traceWriter.Traces); return traces.Contains(expectedMessage); }); var exception = GetExceptionHandler(manager).TimeoutExceptionInfos.Single().SourceException; Assert.IsType<FunctionTimeoutException>(exception); } }
private RunDependencies CreateDependencies(TraceLevel traceLevel = TraceLevel.Info) { var dependencies = new RunDependencies(); var functionTraceWriter = new TestTraceWriter(System.Diagnostics.TraceLevel.Verbose); var traceWriter = new TestTraceWriter(System.Diagnostics.TraceLevel.Verbose); var scriptHostConfiguration = new ScriptHostConfiguration { HostConfig = new JobHostConfiguration(), TraceWriter = traceWriter, FileLoggingMode = FileLoggingMode.Always, FileWatchingEnabled = true }; scriptHostConfiguration.HostConfig.Tracing.ConsoleLevel = System.Diagnostics.TraceLevel.Verbose; var host = new Mock<ScriptHost>(scriptHostConfiguration); host.SetupGet(h => h.IsPrimary).Returns(true); var entrypointResolver = new Mock<IFunctionEntryPointResolver>(); var compilation = new Mock<ICompilation>(); compilation.Setup(c => c.GetDiagnostics()) .Returns(ImmutableArray<Diagnostic>.Empty); var compilationService = new Mock<ICompilationService>(); compilationService.Setup(s => s.SupportedFileTypes) .Returns(() => new[] { ".csx" }); compilationService.Setup(s => s.GetFunctionCompilation(It.IsAny<FunctionMetadata>())) .Returns(compilation.Object); var compilationServiceFactory = new Mock<ICompilationServiceFactory>(); compilationServiceFactory.Setup(f => f.CreateService(ScriptType.CSharp, It.IsAny<IFunctionMetadataResolver>())) .Returns(compilationService.Object); var traceWriterFactory = new Mock<ITraceWriterFactory>(); traceWriterFactory.Setup(f => f.Create()) .Returns(functionTraceWriter); var metricsLogger = new MetricsLogger(); scriptHostConfiguration.HostConfig.AddService<IMetricsLogger>(metricsLogger); return new RunDependencies { Host = host, EntrypointResolver = entrypointResolver, Compilation = compilation, CompilationService = compilationService, CompilationServiceFactory = compilationServiceFactory, TraceWriterFactory = traceWriterFactory, TraceWriter = functionTraceWriter }; }
public void AcquiringLease_WithServerError_LogsAndRetries() { string hostId = Guid.NewGuid().ToString(); string instanceId = Guid.NewGuid().ToString(); var traceWriter = new TestTraceWriter(TraceLevel.Verbose); var renewResetEvent = new ManualResetEventSlim(); var results = new Queue<Task<string>>(); results.Enqueue(Task.FromException<string>(new StorageException(new RequestResult { HttpStatusCode = 500 }, "test", null))); results.Enqueue(Task.FromResult(hostId)); var blobMock = new Mock<ICloudBlob>(); blobMock.Setup(b => b.AcquireLeaseAsync(It.IsAny<TimeSpan>(), It.IsAny<string>())) .Returns(() => results.Dequeue()); blobMock.Setup(b => b.RenewLeaseAsync(It.IsAny<AccessCondition>())) .Returns(() => Task.Delay(1000)) .Callback(() => renewResetEvent.Set()); BlobLeaseManager manager; using (manager = new BlobLeaseManager(blobMock.Object, TimeSpan.FromSeconds(5), hostId, instanceId, traceWriter)) { renewResetEvent.Wait(TimeSpan.FromSeconds(10)); } Assert.True(renewResetEvent.IsSet); Assert.True(manager.HasLease); Assert.True(traceWriter.Traces.Any(t => t.Message.Contains("Server error"))); }
private RunDependencies CreateDependencies(TraceLevel traceLevel = TraceLevel.Info, IScriptHostEnvironment environment = null) { var dependencies = new RunDependencies(); var functionTraceWriter = new TestTraceWriter(System.Diagnostics.TraceLevel.Verbose); var traceWriter = new TestTraceWriter(System.Diagnostics.TraceLevel.Verbose); var scriptHostConfiguration = new ScriptHostConfiguration { HostConfig = new JobHostConfiguration(), TraceWriter = traceWriter, FileLoggingMode = FileLoggingMode.Always, FileWatchingEnabled = true }; scriptHostConfiguration.HostConfig.Tracing.ConsoleLevel = System.Diagnostics.TraceLevel.Verbose; var eventManager = new ScriptEventManager(); var host = new Mock <ScriptHost>(environment ?? new NullScriptHostEnvironment(), eventManager, scriptHostConfiguration, null, null, null); host.CallBase = true; var traceWriterFactory = new Mock <IFunctionTraceWriterFactory>(); traceWriterFactory.Setup(f => f.Create(It.IsAny <string>(), null)) .Returns(functionTraceWriter); host.SetupGet(h => h.IsPrimary).Returns(true); host.SetupGet(h => h.FunctionTraceWriterFactory).Returns(traceWriterFactory.Object); var entrypointResolver = new Mock <IFunctionEntryPointResolver>(); var compilation = new Mock <IDotNetCompilation>(); compilation.Setup(c => c.GetDiagnostics()) .Returns(ImmutableArray <Diagnostic> .Empty); var compilationService = new Mock <ICompilationService <IDotNetCompilation> >(); compilationService.Setup(s => s.SupportedFileTypes) .Returns(() => new[] { ".csx" }); compilationService.Setup(s => s.GetFunctionCompilationAsync(It.IsAny <FunctionMetadata>())) .ReturnsAsync(compilation.Object); var compilationServiceFactory = new Mock <ICompilationServiceFactory <ICompilationService <IDotNetCompilation>, IFunctionMetadataResolver> >(); compilationServiceFactory.Setup(f => f.CreateService(ScriptType.CSharp, It.IsAny <IFunctionMetadataResolver>())) .Returns(compilationService.Object); var metricsLogger = new MetricsLogger(); scriptHostConfiguration.HostConfig.AddService <IMetricsLogger>(metricsLogger); return(new RunDependencies { Host = host, EntrypointResolver = entrypointResolver, Compilation = compilation, CompilationService = compilationService, CompilationServiceFactory = compilationServiceFactory, TraceWriterFactory = traceWriterFactory, TraceWriter = functionTraceWriter }); }
private async Task RunTokenTest(string scenario, Action<IEnumerable<string>> verify) { string functionName = "TimeoutToken"; TestHelpers.ClearFunctionLogs(functionName); var traceWriter = new TestTraceWriter(TraceLevel.Info); using (var manager = await CreateAndStartScriptHostManager("CSharp", functionName, TimeSpan.FromSeconds(3), traceWriter)) { Dictionary<string, object> arguments = new Dictionary<string, object> { { "input", scenario }, }; FunctionTimeoutException ex = await Assert.ThrowsAsync<FunctionTimeoutException>(() => manager.Instance.CallAsync(functionName, arguments)); var exception = GetExceptionHandler(manager).TimeoutExceptionInfos.Single().SourceException; Assert.IsType<FunctionTimeoutException>(exception); verify(traceWriter.Traces.Select(t => t.ToString())); } }
public void TryParseFunctionMetadata_ValidatesHttpRoutes() { // first add an http function JObject functionConfig = new JObject(); functionConfig.Add("bindings", new JArray(new JObject { { "type", "httpTrigger" }, { "name", "req" }, { "direction", "in" }, { "methods", new JArray("get") }, { "route", "products/{category}/{id?}" } })); var mappedHttpFunctions = new Dictionary <string, HttpTriggerBindingMetadata>(); var traceWriter = new TestTraceWriter(TraceLevel.Verbose); var functionFilesProvider = new Lazy <string[]>(() => new string[] { "run.csx" }); FunctionMetadata functionMetadata = null; string functionError = null; bool result = ScriptHost.TryParseFunctionMetadata("test", functionConfig, mappedHttpFunctions, traceWriter, functionFilesProvider, out functionMetadata, out functionError); Assert.True(result); Assert.NotNull(functionMetadata); Assert.Null(functionError); Assert.Equal(1, mappedHttpFunctions.Count); Assert.True(mappedHttpFunctions.ContainsKey("test")); Assert.Equal("run.csx", functionMetadata.ScriptFile); // add another for a completely different route functionConfig["bindings"] = new JArray(new JObject { { "type", "httpTrigger" }, { "name", "req" }, { "direction", "in" }, { "methods", new JArray("get") }, { "route", "/foo/bar/baz/" } }); functionMetadata = null; functionError = null; result = ScriptHost.TryParseFunctionMetadata("test2", functionConfig, mappedHttpFunctions, traceWriter, functionFilesProvider, out functionMetadata, out functionError); Assert.True(result); Assert.NotNull(functionMetadata); Assert.Null(functionError); Assert.True(mappedHttpFunctions.ContainsKey("test2")); Assert.Equal(2, mappedHttpFunctions.Count); // add another that varies from another only by http method functionConfig["bindings"] = new JArray(new JObject { { "type", "httpTrigger" }, { "name", "req" }, { "direction", "in" }, { "methods", new JArray("put", "post") }, { "route", "/foo/bar/baz" } }); functionMetadata = null; functionError = null; result = ScriptHost.TryParseFunctionMetadata("test3", functionConfig, mappedHttpFunctions, traceWriter, functionFilesProvider, out functionMetadata, out functionError); Assert.True(result); Assert.NotNull(functionMetadata); Assert.Null(functionError); Assert.True(mappedHttpFunctions.ContainsKey("test3")); Assert.Equal(3, mappedHttpFunctions.Count); // now try to add a function for the same route // where the http methods overlap functionConfig["bindings"] = new JArray(new JObject { { "type", "httpTrigger" }, { "name", "req" }, { "direction", "in" }, { "route", "foo/bar/baz" } }); functionMetadata = null; functionError = null; result = ScriptHost.TryParseFunctionMetadata("test4", functionConfig, mappedHttpFunctions, traceWriter, functionFilesProvider, out functionMetadata, out functionError); Assert.False(result); Assert.NotNull(functionMetadata); Assert.True(functionError.StartsWith("The route specified conflicts with the route defined by function")); Assert.Equal(3, mappedHttpFunctions.Count); // try to add a route under reserved admin route functionConfig["bindings"] = new JArray(new JObject { { "type", "httpTrigger" }, { "name", "req" }, { "direction", "in" }, { "route", "admin/foo/bar" } }); functionMetadata = null; functionError = null; result = ScriptHost.TryParseFunctionMetadata("test5", functionConfig, mappedHttpFunctions, traceWriter, functionFilesProvider, out functionMetadata, out functionError); Assert.False(result); Assert.NotNull(functionMetadata); Assert.Equal(3, mappedHttpFunctions.Count); Assert.Equal("The specified route conflicts with one or more built in routes.", functionError); }
public void GeneratedMethods_WithOutParams_DoNotCauseDeadlocks() { var traceWriter = new TestTraceWriter(TraceLevel.Verbose); ScriptHostConfiguration config = new ScriptHostConfiguration() { RootScriptPath = @"TestScripts\FunctionGeneration", TraceWriter = traceWriter }; using (var manager = new WebScriptHostManager(config)) { Thread runLoopThread = new Thread(_ => { manager.RunAndBlock(CancellationToken.None); }); runLoopThread.IsBackground = true; runLoopThread.Start(); while (!manager.IsRunning) { Thread.Sleep(100); } FunctionDescriptor function = manager.GetHttpFunctionOrNull(new Uri("http://localhost/api/httptrigger-csharp")); var request = new HttpRequestMessage(HttpMethod.Get, "http://localhost/api/httptrigger-csharp"); SynchronizationContext currentContext = SynchronizationContext.Current; var resetEvent = new ManualResetEventSlim(); try { var requestThread = new Thread(() => { var context = new SingleThreadSynchronizationContext(); SynchronizationContext.SetSynchronizationContext(context); manager.HandleRequestAsync(function, request, CancellationToken.None) .ContinueWith(task => resetEvent.Set()); Thread.Sleep(500); context.Run(); }); requestThread.IsBackground = true; requestThread.Start(); bool threadSignaled = resetEvent.Wait(TimeSpan.FromSeconds(10)); requestThread.Abort(); Assert.True(threadSignaled, "Thread execution did not complete"); } finally { SynchronizationContext.SetSynchronizationContext(currentContext); manager.Stop(); } } }