protected EndToEndTestFixture(string rootPath) { CreateTestStorageEntities(); TraceWriter = new TestTraceWriter(TraceLevel.Verbose); ScriptHostConfiguration config = new ScriptHostConfiguration() { RootScriptPath = rootPath, TraceWriter = TraceWriter }; Host = ScriptHost.Create(config); Host.Start(); }
public void GetFunctionSecrets_UpdatesStaleSecrets() { using (var directory = new TempDirectory()) { string functionName = "testfunction"; string expectedTraceMessage = string.Format(Resources.TraceStaleFunctionSecretRefresh, functionName); string functionSecretsJson = @"{ 'keys': [ { 'name': 'Key1', 'value': 'FunctionValue1', 'encrypted': false }, { 'name': 'Key2', 'value': 'FunctionValue2', 'encrypted': false } ] }"; File.WriteAllText(Path.Combine(directory.Path, functionName + ".json"), functionSecretsJson); Mock<IKeyValueConverterFactory> mockValueConverterFactory = GetConverterFactoryMock(); IDictionary<string, string> functionSecrets; var traceWriter = new TestTraceWriter(TraceLevel.Verbose); using (var secretManager = new SecretManager(directory.Path, mockValueConverterFactory.Object, traceWriter)) { functionSecrets = secretManager.GetFunctionSecrets(functionName); } // Read the persisted content var result = JsonConvert.DeserializeObject<FunctionSecrets>(File.ReadAllText(Path.Combine(directory.Path, functionName + ".json"))); bool functionSecretsConverted = functionSecrets.Values.Zip(result.Keys, (r1, r2) => string.Equals("!" + r1, r2.Value)).All(r => r); Assert.Equal(2, result.Keys.Count); Assert.True(functionSecretsConverted, "Function secrets were not persisted"); Assert.True(traceWriter.Traces.Any(t => t.Level == TraceLevel.Verbose && t.Message.IndexOf(expectedTraceMessage) > -1)); } }
public void Constructor_WithCreateHostSecretsIfMissingSet_CreatesHostSecret() { var secretsPath = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()); var hostSecretPath = Path.Combine(secretsPath, ScriptConstants.HostMetadataFileName); try { string expectedTraceMessage = Resources.TraceHostSecretGeneration; bool preExistingFile = File.Exists(hostSecretPath); Mock<IKeyValueConverterFactory> mockValueConverterFactory = GetConverterFactoryMock(false, false); var traceWriter = new TestTraceWriter(TraceLevel.Verbose); var secretManager = new SecretManager(secretsPath, mockValueConverterFactory.Object, traceWriter, true); bool fileCreated = File.Exists(hostSecretPath); Assert.False(preExistingFile); Assert.True(fileCreated); Assert.True(traceWriter.Traces.Any(t => t.Level == TraceLevel.Verbose && t.Message.IndexOf(expectedTraceMessage) > -1)); } finally { Directory.Delete(secretsPath, true); } }
public void SetMasterKey_WithoutProvidedKey_GeneratesKeyAndPersistsFile() { using (var directory = new TempDirectory()) { string expectedTraceMessage = "Master key Created"; Mock<IKeyValueConverterFactory> mockValueConverterFactory = GetConverterFactoryMock(false); KeyOperationResult result; var traceWriter = new TestTraceWriter(TraceLevel.Verbose); using (var secretManager = new SecretManager(directory.Path, mockValueConverterFactory.Object, traceWriter)) { result = secretManager.SetMasterKey(); } bool functionSecretsExists = File.Exists(Path.Combine(directory.Path, "testfunction.json")); string secretsJson = File.ReadAllText(Path.Combine(directory.Path, ScriptConstants.HostMetadataFileName)); HostSecrets persistedSecrets = ScriptSecretSerializer.DeserializeSecrets<HostSecrets>(secretsJson); Assert.NotNull(persistedSecrets); Assert.NotNull(persistedSecrets.MasterKey); Assert.Equal(OperationResult.Created, result.Result); Assert.Equal(result.Secret, persistedSecrets.MasterKey.Value); Assert.True(traceWriter.Traces.Any(t => t.Level == TraceLevel.Info && t.Message.IndexOf(expectedTraceMessage) > -1), "Expected Trace message not found"); } }
public void AddOrUpdateFunctionSecrets_WithNoFunctionNameAndProvidedSecret_UsesSecretAndPersistsHostFile() { using (var directory = new TempDirectory()) { string secretName = "TestSecret"; string expectedTraceMessage = string.Format(Resources.TraceAddOrUpdateFunctionSecret, "Host", secretName, "host", "Created"); Mock<IKeyValueConverterFactory> mockValueConverterFactory = GetConverterFactoryMock(false); KeyOperationResult result; var traceWriter = new TestTraceWriter(TraceLevel.Verbose); using (var secretManager = new SecretManager(directory.Path, mockValueConverterFactory.Object, traceWriter)) { result = secretManager.AddOrUpdateFunctionSecret(secretName, "TestSecretValue"); } string secretsJson = File.ReadAllText(Path.Combine(directory.Path, ScriptConstants.HostMetadataFileName)); HostSecrets persistedSecrets = ScriptSecretSerializer.DeserializeSecrets<HostSecrets>(secretsJson); Key newSecret = persistedSecrets.FunctionKeys.FirstOrDefault(k => string.Equals(k.Name, secretName, StringComparison.Ordinal)); Assert.Equal(OperationResult.Created, result.Result); Assert.Equal("TestSecretValue", result.Secret, StringComparer.Ordinal); Assert.NotNull(persistedSecrets); Assert.NotNull(newSecret); Assert.Equal(result.Secret, newSecret.Value); Assert.Equal(secretName, newSecret.Name, StringComparer.Ordinal); Assert.NotNull(persistedSecrets.MasterKey); Assert.True(traceWriter.Traces.Any(t => t.Level == TraceLevel.Info && t.Message.IndexOf(expectedTraceMessage) > -1), "Expected Trace message not found"); } }
public void AddOrUpdateFunctionSecrets_WithFunctionNameAndNoSecret_GeneratesFunctionSecretsAndPersistsFile() { using (var directory = new TempDirectory()) { string secretName = "TestSecret"; string functionName = "TestFunction"; string expectedTraceMessage = string.Format(Resources.TraceAddOrUpdateFunctionSecret, "Function", secretName, functionName, "Created"); Mock<IKeyValueConverterFactory> mockValueConverterFactory = GetConverterFactoryMock(false); KeyOperationResult result; var traceWriter = new TestTraceWriter(TraceLevel.Verbose); using (var secretManager = new SecretManager(directory.Path, mockValueConverterFactory.Object, traceWriter)) { result = secretManager.AddOrUpdateFunctionSecret(secretName, null, functionName); } string secretsJson = File.ReadAllText(Path.Combine(directory.Path, "testfunction.json")); FunctionSecrets persistedSecrets = ScriptSecretSerializer.DeserializeSecrets<FunctionSecrets>(secretsJson); Assert.Equal(OperationResult.Created, result.Result); Assert.NotNull(result.Secret); Assert.NotNull(persistedSecrets); Assert.Equal(result.Secret, persistedSecrets.Keys.First().Value); Assert.Equal(secretName, persistedSecrets.Keys.First().Name, StringComparer.Ordinal); Assert.True(traceWriter.Traces.Any(t => t.Level == TraceLevel.Info && t.Message.IndexOf(expectedTraceMessage) > -1), "Expected Trace message not found"); } }
public void GetFunctionSecrets_WhenNoSecretFileExists_CreatesDefaultSecretAndPersistsFile() { using (var directory = new TempDirectory()) { string functionName = "TestFunction"; string expectedTraceMessage = string.Format(Resources.TraceFunctionSecretGeneration, functionName); Mock<IKeyValueConverterFactory> mockValueConverterFactory = GetConverterFactoryMock(false, false); IDictionary<string, string> functionSecrets; var traceWriter = new TestTraceWriter(TraceLevel.Verbose); using (var secretManager = new SecretManager(directory.Path, mockValueConverterFactory.Object, traceWriter)) { functionSecrets = secretManager.GetFunctionSecrets(functionName); } bool functionSecretsExists = File.Exists(Path.Combine(directory.Path, "testfunction.json")); Assert.NotNull(functionSecrets); Assert.True(functionSecretsExists); Assert.Equal(1, functionSecrets.Count); Assert.Equal(ScriptConstants.DefaultFunctionKeyName, functionSecrets.Keys.First()); Assert.True(traceWriter.Traces.Any( t => t.Level == TraceLevel.Verbose && t.Message.IndexOf(expectedTraceMessage, StringComparison.OrdinalIgnoreCase) > -1), "Expected Trace message not found"); } }
public void GetHostSecrets_WhenNoHostSecretFileExists_GeneratesSecretsAndPersistsFiles() { using (var directory = new TempDirectory()) { string expectedTraceMessage = Resources.TraceHostSecretGeneration; Mock<IKeyValueConverterFactory> mockValueConverterFactory = GetConverterFactoryMock(false, false); HostSecretsInfo hostSecrets; var traceWriter = new TestTraceWriter(TraceLevel.Verbose); using (var secretManager = new SecretManager(directory.Path, mockValueConverterFactory.Object, traceWriter)) { hostSecrets = secretManager.GetHostSecrets(); } string secretsJson = File.ReadAllText(Path.Combine(directory.Path, ScriptConstants.HostMetadataFileName)); HostSecrets persistedSecrets = ScriptSecretSerializer.DeserializeSecrets<HostSecrets>(secretsJson); Assert.NotNull(hostSecrets); Assert.NotNull(persistedSecrets); Assert.Equal(1, hostSecrets.FunctionKeys.Count); Assert.NotNull(hostSecrets.MasterKey); Assert.Equal(persistedSecrets.MasterKey.Value, hostSecrets.MasterKey); Assert.Equal(persistedSecrets.FunctionKeys.First().Value, hostSecrets.FunctionKeys.First().Value); Assert.True(traceWriter.Traces.Any(t => t.Level == TraceLevel.Verbose && t.Message.IndexOf(expectedTraceMessage) > -1)); } }
public void GetHostSecrets_UpdatesStaleSecrets() { using (var directory = new TempDirectory()) { string expectedTraceMessage = Resources.TraceStaleHostSecretRefresh; string hostSecretsJson = @"{ 'masterKey': { 'name': 'master', 'value': '1234', 'encrypted': false }, 'functionKeys': [ { 'name': 'Key1', 'value': 'HostValue1', 'encrypted': false }, { 'name': 'Key3', 'value': 'HostValue3', 'encrypted': false } ] }"; File.WriteAllText(Path.Combine(directory.Path, ScriptConstants.HostMetadataFileName), hostSecretsJson); Mock<IKeyValueConverterFactory> mockValueConverterFactory = GetConverterFactoryMock(); HostSecretsInfo hostSecrets; var traceWriter = new TestTraceWriter(TraceLevel.Verbose); using (var secretManager = new SecretManager(directory.Path, mockValueConverterFactory.Object, traceWriter)) { hostSecrets = secretManager.GetHostSecrets(); } // Read the persisted content var result = JsonConvert.DeserializeObject<HostSecrets>(File.ReadAllText(Path.Combine(directory.Path, ScriptConstants.HostMetadataFileName))); bool functionSecretsConverted = hostSecrets.FunctionKeys.Values.Zip(result.FunctionKeys, (r1, r2) => string.Equals("!" + r1, r2.Value)).All(r => r); Assert.Equal(2, result.FunctionKeys.Count); Assert.Equal("!" + hostSecrets.MasterKey, result.MasterKey.Value); Assert.True(functionSecretsConverted, "Function secrets were not persisted"); Assert.True(traceWriter.Traces.Any(t => t.Level == TraceLevel.Verbose && t.Message.IndexOf(expectedTraceMessage) > -1)); } }
public async Task RecoveryTest(int expectedNumberOfAttempts, bool isFailureScenario) { var traceWriter = new TestTraceWriter(TraceLevel.Verbose); using (var directory = new TempDirectory()) using (var watcher = new AutoRecoveringFileSystemWatcher(directory.Path, traceWriter: traceWriter)) { Directory.Delete(directory.Path, true); string fileWatcherLogPrefix = $"File watcher: ('{directory.Path}')"; // 1 trace per attempt + 1 trace per failed attempt int expectedTracesBeforeRecovery = (expectedNumberOfAttempts * 2) - 1; // Before + recovery trace int expectedTracesAfterRecovery = expectedTracesBeforeRecovery + 1; await TestHelpers.Await(() => traceWriter.Traces.Count == expectedTracesBeforeRecovery, pollingInterval: 500); if (isFailureScenario) { watcher.Dispose(); } else { Directory.CreateDirectory(directory.Path); } await TestHelpers.Await(() => traceWriter.Traces.Count == expectedTracesAfterRecovery, pollingInterval: 500); TraceEvent failureEvent = traceWriter.Traces.First(); var retryEvents = traceWriter.Traces.Where(t => t.Level == TraceLevel.Warning).Skip(1).ToList(); Assert.Equal(TraceLevel.Warning, failureEvent.Level); Assert.Contains("Failure detected", failureEvent.Message); Assert.Equal(expectedNumberOfAttempts - 1, retryEvents.Count); // Validate that our the events happened with the expected intervals DateTime previoustTimeStamp = failureEvent.Timestamp; for (int i = 0; i < retryEvents.Count; i++) { long expectedInterval = Convert.ToInt64((Math.Pow(2, i + 1) - 1) / 2); TraceEvent currentEvent = retryEvents[i]; var actualInterval = currentEvent.Timestamp - previoustTimeStamp; previoustTimeStamp = currentEvent.Timestamp; Assert.Equal(expectedInterval, (int)actualInterval.TotalSeconds); } Assert.True(traceWriter.Traces.All(t => t.Message.StartsWith(fileWatcherLogPrefix))); if (isFailureScenario) { Assert.Contains("Recovery process aborted.", traceWriter.Traces.Last().Message); } else { Assert.Contains("File watcher recovered.", traceWriter.Traces.Last().Message); } } }
public void FileWatcherTest(string path, Action<AutoRecoveringFileSystemWatcher> action, Func<FileSystemEventArgs, bool> changeHandler, WatcherChangeTypes changeTypes = WatcherChangeTypes.All, bool expectEvent = true) { var traceWriter = new TestTraceWriter(System.Diagnostics.TraceLevel.Verbose); using (var watcher = new AutoRecoveringFileSystemWatcher(path, traceWriter: traceWriter)) { var resetEvent = new ManualResetEventSlim(); watcher.Changed += (s, a) => { if (changeHandler(a)) { resetEvent.Set(); } }; action(watcher); bool eventSignaled = resetEvent.Wait(TimeSpan.FromSeconds(5)); Assert.Equal(expectEvent, eventSignaled); } }