Last active
August 13, 2021 17:34
-
-
Save justinyoo/c06c3b4df77d4fc6075f924e19ec0d6a to your computer and use it in GitHub Desktop.
Azure Functions SRE, The First Cut
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # Release Pipeline | |
| - stage: Release | |
| jobs: | |
| - deployment: HostedVs2017 | |
| ... | |
| variables: | |
| - name: TestRunStatus | |
| value: '' | |
| strategy: | |
| runOnce: | |
| deploy: | |
| steps: | |
| ... | |
| - task: CmdLine@2 | |
| displayName: 'Run E2E Tests' | |
| inputs: | |
| script: 'dotnet vstest $(Pipeline.Workspace)\e2e\FunctionApp.Tests\FunctionApp.Tests.dll --testCaseFilter:TestCategory=E2E --logger:trx --resultsDirectory:$(Pipeline.Workspace)\TestResults' | |
| continueOnError: true | |
| env: | |
| ServerName: FunctionApp | |
| FunctionAuthKey: $(FunctionAuthKey) | |
| FunctionAppName: $(FunctionAppName) | |
| - task: PowerShell@2 | |
| displayName: 'Save Test Run Status' | |
| inputs: | |
| targetType: Inline | |
| script: 'Write-Host "##vso[task.setvariable variable=TestRunStatus]$(Agent.JobStatus)"' | |
| - task: PublishTestResults@2 | |
| displayName: 'Publish E2E Test Results' | |
| inputs: | |
| testRunTitle: 'E2E Tests' | |
| testResultsFormat: VSTest | |
| testResultsFiles: '$(Pipeline.Workspace)/TestResults/*.trx' | |
| mergeTestResults: true | |
| - task: PowerShell@2 | |
| displayName: 'Cancel Pipeline on Test Run Failure' | |
| condition: and(succeeded(), or(eq(variables['TestRunStatus'], 'Failed'), eq(variables['TestRunStatus'], 'SucceededWithIssues'))) | |
| inputs: | |
| targetType: Inline | |
| script: | | |
| Write-Host "##vso[task.setvariable variable=Agent.JobStatus]Failed" | |
| Write-Host "##vso[task.complete result=Failed]DONE" |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| dotnet test [Test_Project_Name].csproj -c Release --filter:"TestCategory=E2E" |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| set ServerName=FunctionApp | |
| set FunctionAuthKey=uCQISIGoaMYz6d/6yR/Q2aw6PVdxLhzOh1gy9IjfoRbTN9OgmOFgTQ== | |
| set FunctionAppName=fncapp-mountebank |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| public class FunctionAppServerFixture : ServerFixture | |
| { | |
| private const string FunctionAppNameKey = "FunctionAppName"; | |
| private const string FunctionAuthKeyKey = "FunctionAuthKey"; | |
| private readonly string _functionAppName; | |
| private readonly string _functionAuthKey; | |
| public FunctionAppServerFixture() | |
| { | |
| this._functionAppName = Environment.GetEnvironmentVariable(FunctionAppNameKey); | |
| this._functionAuthKey = Environment.GetEnvironmentVariable(FunctionAuthKeyKey); | |
| } | |
| public override string GetHealthCheckUrl(HttpStatusCode statusCode = HttpStatusCode.OK) | |
| { | |
| return $"https://{this._functionAppName}.azurewebsites.net/api/ping?code={this._functionAuthKey}"; | |
| } | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| public interface IHealthCheckFunction : IFunction<ILogger> | |
| { | |
| } | |
| public class HealthCheckFunction : FunctionBase<ILogger>, IHealthCheckFunction | |
| { | |
| ... | |
| // Dependency injections here | |
| public override async Task<TOutput> InvokeAsync<TInput, TOutput>( | |
| TInput input, | |
| functionOptionsBase options = null) | |
| { | |
| var result = (IActionResult)null; | |
| var requestUri = $"{this._settings.BaseUri.TrimEnd('/')}/{this._settings.Endpoints.HealthCheck.TrimStart('/')}"; | |
| using (var response = await this._httpClient.GetAsync(requestUri).ConfigureAwaitfalse)) | |
| { | |
| try | |
| { | |
| response.EnsureSuccessStatusCode(); | |
| result = new OkResult(); | |
| } | |
| catch (Exception ex) | |
| { | |
| var error = new ErrorResponse(ex); | |
| result = new ObjectResult(error) { StatusCode = (int)response.StatusCode }; | |
| } | |
| } | |
| return (TOutput)result; | |
| } | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| [TestClass] | |
| public class HealthCheckHttpTriggerTests | |
| { | |
| private const string CategoryIntegration = "Integration"; | |
| private const string CategoryE2E = "E2E"; | |
| private const string DefaultServerName = "Localhost"; | |
| private const string ServerNameKey = "ServerName"; | |
| private ServerFixture _fixture; | |
| [TestInitialize] | |
| public void Init() | |
| { | |
| // Gets the server name from the environment variable. | |
| var serverName = Environment.GetEnvironmentVariable(ServerNameKey); | |
| if (string.IsNullOrWhiteSpace(serverName)) | |
| { | |
| serverName = DefaultServerName; | |
| } | |
| // Creates the fixture from the factory method pattern. | |
| this._fixture = ServerFixture.CreateInstance(serverName); | |
| } | |
| ... | |
| [TestMethod] | |
| [TestCategory(CategoryIntegration)] | |
| // Adds the test category for end-to-end testing | |
| [TestCategory(CategoryE2E)] | |
| public async Task Given_Url_When_Invoked_Then_Trigger_Should_Return_Healthy() | |
| { | |
| ... | |
| } | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| public class HealthCheckHttpTrigger | |
| { | |
| ... | |
| // Dependency injections here | |
| [FunctionName(nameof(HealthCheckHttpTrigger.PingAsync))] | |
| public async Task<IActionResult> PingAsync( | |
| [HttpTrigger(AuthorizationLevel.Function, "get", Route = "ping")] HttpRequest req, | |
| ILogger log) | |
| { | |
| return result = await this._function | |
| .InvokeAsync<HttpRequest, IActionResult>(req) | |
| .ConfigureAwait(false); | |
| } | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| npm install -g mountebank |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| stages: | |
| # Build Pipeline | |
| - stage: Build | |
| jobs: | |
| - job: HostedVs2017 | |
| ... | |
| variables: | |
| - name: IntegrationTestRunStatus | |
| value: '' | |
| steps: | |
| ... | |
| - task: CmdLine@2 | |
| displayName: 'Integration Test Function App' | |
| inputs: | |
| script: 'dotnet test $(Build.SourcesDirectory)\test\FunctionApp.Tests\FunctionApp.Tests.csproj -c $(BuildConfiguration) --filter:"TestCategory=Integration" --logger:trx --results-directory:$(System.DefaultWorkingDirectory)\IntegrationTestResults' | |
| env: | |
| ServerName: Localhost | |
| continueOnError: true | |
| - task: PowerShell@2 | |
| displayName: 'Save Integration Test Run Status' | |
| inputs: | |
| targetType: Inline | |
| script: 'Write-Host "##vso[task.setvariable variable=IntegrationTestRunStatus]$(Agent.JobStatus)"' | |
| - task: PublishTestResults@2 | |
| displayName: 'Publish Integration Test Results' | |
| inputs: | |
| testRunTitle: 'Integration Tests' | |
| testResultsFormat: VSTest | |
| testResultsFiles: '$(System.DefaultWorkingDirectory)/IntegrationTestResults/*.trx' | |
| mergeTestResults: true | |
| - task: PowerShell@2 | |
| displayName: 'Cancel Pipeline on Test Run Failure' | |
| condition: and(succeeded(), or(eq(variables['UnitTestRunStatus'], 'Failed'), eq(variables['UnitTestRunStatus'], 'SucceededWithIssues'), eq(variables['IntegrationTestRunStatus'], 'Failed'), eq(variables['IntegrationTestRunStatus'], 'SucceededWithIssues'))) | |
| inputs: | |
| targetType: Inline | |
| script: | | |
| Write-Host "##vso[task.setvariable variable=Agent.JobStatus]Failed" | |
| Write-Host "##vso[task.complete result=Failed]DONE" | |
| ... |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| dotnet test [Test_Project_Name].csproj -c Release --filter:"TestCategory=Integration" |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| [TestClass] | |
| public class HealthCheckHttpTriggerTests | |
| { | |
| private const string CategoryIntegration = "Integration"; | |
| private ServerFixture _fixture; | |
| [TestInitialize] | |
| public void Init() | |
| { | |
| this._fixture = new LocalhostServerFixture(); | |
| } | |
| [TestMethod] | |
| [TestCategory(CategoryIntegration)] | |
| public async Task Given_Url_When_Invoked_Then_Trigger_Should_Return_Healthy() | |
| { | |
| // Arrange | |
| var uri = this._fixture.GetHealthCheckUrl(); | |
| using (var http = new HttpClient()) | |
| // Act | |
| using (var res = await http.GetAsync(uri)) | |
| { | |
| // Assert | |
| res.StatusCode.Should().Be(HttpStatusCode.OK); | |
| } | |
| } | |
| [TestMethod] | |
| [TestCategory(CategoryIntegration)] | |
| public async Task Given_Url_When_Invoked_Then_Trigger_Should_Return_Unhealthy() | |
| { | |
| // Arrange | |
| var uri = this._fixture.GetHealthCheckUrl(HttpStatusCode.InternalServerError); | |
| using (var http = new HttpClient()) | |
| // Act | |
| using (var res = await http.GetAsync(uri)) | |
| { | |
| // Assert | |
| res.StatusCode.Should().Be(HttpStatusCode.InternalServerError); | |
| } | |
| } | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| public class LocalhostServerFixture : ServerFixture | |
| { | |
| ... | |
| public override string GetHealthCheckUrl(HttpStatusCode statusCode = HttpStatusCode.OK) | |
| { | |
| ... | |
| } | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| public class LocalhostServerFixture | |
| { | |
| private readonly MountebankClient _client; | |
| public MountebankServerFixture() | |
| { | |
| this._client = new MountebankClient(); | |
| } | |
| public string GetHealthCheckUrl(HttpStatusCode statusCode = HttpStatusCode.OK) | |
| { | |
| this._client.DeleteImposter(8080); | |
| var imposter = this._client | |
| .CreateHttpImposter(8080, statusCode.ToString()); | |
| imposter.AddStub() | |
| .OnPathAndMethodEqual("/api/ping", Method.Get) | |
| .ReturnsStatus(statusCode); | |
| this._client.Submit(imposter); | |
| return "http://localhost:7071/api/ping"; | |
| } | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| mb |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| start /b mb --noLogFile | |
| start /b func host start --csharp |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # Console #1 | |
| mb --noLogFile | |
| # Console #2 | |
| func host start --csharp |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| public abstract class ServerFixture | |
| { | |
| public static ServerFixture CreateInstance(string serverName) | |
| { | |
| var type = Type.GetType($"FunctionApp.Tests.Fixtures.{serverName}ServerFixture"); | |
| var instance = (ServerFixture)Activator.CreateInstance(type); | |
| return instance; | |
| } | |
| public abstract string GetHealthCheckUrl(HttpStatusCode statusCode = HttpStatusCode.OK); | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| stages: | |
| # Build Pipeline | |
| - stage: Build | |
| jobs: | |
| - job: HostedVs2017 | |
| ... | |
| variables: | |
| - name: UnitTestRunStatus | |
| value: '' | |
| steps: | |
| ... | |
| - task: CmdLine@2 | |
| displayName: 'Unit Test Function App' | |
| inputs: | |
| script: 'dotnet test $(Build.SourcesDirectory)\test\FunctionApp.Tests\FunctionApp.Tests.csproj -c $(BuildConfiguration) --filter:"TestCategory!=Integration&TestCategory!=E2E" --logger:trx --results-directory:$(System.DefaultWorkingDirectory)\UnitTestResults /p:CollectCoverage=true /p:CoverletOutputFormat=cobertura /p:CoverletOutput=$(System.DefaultWorkingDirectory)\CoverageResults\coverage' | |
| env: | |
| ServerName: Localhost | |
| continueOnError: true | |
| - task: PowerShell@2 | |
| displayName: 'Save Unit Test Run Status' | |
| inputs: | |
| targetType: Inline | |
| script: 'Write-Host "##vso[task.setvariable variable=UnitTestRunStatus]$(Agent.JobStatus)"' | |
| - task: PublishTestResults@2 | |
| displayName: 'Publish Unit Test Results' | |
| inputs: | |
| testRunTitle: 'Unit Tests' | |
| testResultsFormat: VSTest | |
| testResultsFiles: '$(System.DefaultWorkingDirectory)/UnitTestResults/*.trx' | |
| mergeTestResults: true | |
| - task: PublishCodeCoverageResults@1 | |
| displayName: 'Publish Code Coverage Results' | |
| inputs: | |
| codeCoverageTool: 'cobertura' | |
| summaryFileLocation: '$(System.DefaultWorkingDirectory)/CoverageResults/*.xml' | |
| failIfCoverageEmpty: false | |
| ... |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| dotnet test [Test_Project_Name].csproj -c Release |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| [TestMethod] | |
| public async Task Given_Parameters_When_Invoked_Then_InvokeAsync_Should_Return_Result() | |
| { | |
| // Arrange | |
| var result = new OkResult(); | |
| var function = new Mock<IHealthCheckFunction>(); | |
| function.Setup(p => p.InvokeAsync<HttpRequest, IActionResult>(It.IsAny<HttpRequest>(), It.IsAny<FunctionOptionsBase>())) | |
| .ReturnsAsync(result); | |
| var trigger = new HealthCheckHttpTrigger(function.Object); | |
| var req = new Mock<HttpRequest>(); | |
| var log = new Mock<ILogger>(); | |
| // Action | |
| var response = await trigger.PingAsync(req.Object, log.Object).ConfigureAwait(false); | |
| // Assert | |
| response | |
| .Should().BeOfType<OkResult>() | |
| .And.Subject.As<OkResult>() | |
| .StatusCode.Should().Be((int)HttpStatusCode.OK); | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| [TestMethod] | |
| public async Task Given_Parameters_When_Invoked_Then_InvokeAsync_Should_Return_Result() | |
| { | |
| // Arrange | |
| var result = new OkResult(); | |
| var function = new Mock<IHealthCheckFunction>(); | |
| function.Setup(p => p.InvokeAsync<HttpRequest, IActionResult>(It.IsAny<HttpRequest>(), It.IsAny<FunctionOptionsBase>())) | |
| .ReturnsAsync(result); | |
| var trigger = new HealthCheckHttpTrigger(function.Object); | |
| var req = new Mock<HttpRequest>(); | |
| var log = new Mock<ILogger>(); | |
| // Action | |
| var response = await trigger.PingAsync(req.Object, log.Object).ConfigureAwait(false); | |
| // Assert | |
| response | |
| .Should().BeOfType<OkResult>() | |
| .And.Subject.As<OkResult>() | |
| .StatusCode.Should().Be((int)HttpStatusCode.OK); | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment