Skip to content

Instantly share code, notes, and snippets.

@justinyoo
Last active August 13, 2021 17:34
Show Gist options
  • Select an option

  • Save justinyoo/c06c3b4df77d4fc6075f924e19ec0d6a to your computer and use it in GitHub Desktop.

Select an option

Save justinyoo/c06c3b4df77d4fc6075f924e19ec0d6a to your computer and use it in GitHub Desktop.

Revisions

  1. justinyoo revised this gist Jul 24, 2019. 1 changed file with 1 addition and 0 deletions.
    1 change: 1 addition & 0 deletions health-check-http-trigger-tests-revised.cs
    Original file line number Diff line number Diff line change
    @@ -27,6 +27,7 @@ public void Init()

    [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()
    {
  2. justinyoo revised this gist Jul 24, 2019. 1 changed file with 3 additions and 0 deletions.
    3 changes: 3 additions & 0 deletions health-check-http-trigger-tests-revised.cs
    Original file line number Diff line number Diff line change
    @@ -12,11 +12,14 @@ public class HealthCheckHttpTriggerTests
    [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);
    }

  3. justinyoo renamed this gist Jul 24, 2019. 1 changed file with 0 additions and 0 deletions.
    File renamed without changes.
  4. justinyoo revised this gist Jul 24, 2019. 9 changed files with 197 additions and 0 deletions.
    43 changes: 43 additions & 0 deletions e2e-test-build.yaml
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,43 @@
    # 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"
    3 changes: 3 additions & 0 deletions e2e-test-environment-variables.sh
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,3 @@
    set ServerName=FunctionApp
    set FunctionAuthKey=uCQISIGoaMYz6d/6yR/Q2aw6PVdxLhzOh1gy9IjfoRbTN9OgmOFgTQ==
    set FunctionAppName=fncapp-mountebank
    1 change: 1 addition & 0 deletions e2e-test.sh
    Original file line number Diff line number Diff line change
    @@ -0,0 +1 @@
    dotnet test [Test_Project_Name].csproj -c Release --filter:"TestCategory=E2E"
    19 changes: 19 additions & 0 deletions functionapp-server-fixture.cs
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,19 @@
    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}";
    }
    }
    32 changes: 32 additions & 0 deletions health-check-http-trigger-tests-revised.cs
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,32 @@
    [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()
    {
    var serverName = Environment.GetEnvironmentVariable(ServerNameKey);
    if (string.IsNullOrWhiteSpace(serverName))
    {
    serverName = DefaultServerName;
    }
    this._fixture = ServerFixture.CreateInstance(serverName);
    }

    ...

    [TestMethod]
    [TestCategory(CategoryIntegration)]
    [TestCategory(CategoryE2E)]
    public async Task Given_Url_When_Invoked_Then_Trigger_Should_Return_Healthy()
    {
    ...
    }
    }
    40 changes: 40 additions & 0 deletions integration-test-build.yaml
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,40 @@
    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"
    ...
    9 changes: 9 additions & 0 deletions localhost-server-fixture-revised.cs
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,9 @@
    public class LocalhostServerFixture : ServerFixture
    {
    ...

    public override string GetHealthCheckUrl(HttpStatusCode statusCode = HttpStatusCode.OK)
    {
    ...
    }
    }
    12 changes: 12 additions & 0 deletions server-fixture.cs
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,12 @@
    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);
    }
    38 changes: 38 additions & 0 deletions unit-test-build.yaml
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,38 @@
    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
    ...
  5. justinyoo revised this gist Jul 24, 2019. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion mountebank-server-fixture.cs
    Original file line number Diff line number Diff line change
    @@ -7,7 +7,7 @@ public MountebankServerFixture()
    this._client = new MountebankClient();
    }

    public override string GetHealthCheckUrl(HttpStatusCode statusCode = HttpStatusCode.OK)
    public string GetHealthCheckUrl(HttpStatusCode statusCode = HttpStatusCode.OK)
    {
    this._client.DeleteImposter(8080);

  6. justinyoo revised this gist Jul 24, 2019. 2 changed files with 2 additions and 2 deletions.
    2 changes: 1 addition & 1 deletion integration-test-http-trigger.cs
    Original file line number Diff line number Diff line change
    @@ -7,7 +7,7 @@ public class HealthCheckHttpTriggerTests
    [TestInitialize]
    public void Init()
    {
    this._fixture = new MountebankServerFixture();
    this._fixture = new LocalhostServerFixture();
    }

    [TestMethod]
    2 changes: 1 addition & 1 deletion mountebank-server-fixture.cs
    Original file line number Diff line number Diff line change
    @@ -1,4 +1,4 @@
    public class MountebankServerFixture
    public class LocalhostServerFixture
    {
    private readonly MountebankClient _client;

  7. justinyoo revised this gist Jul 21, 2019. 1 changed file with 8 additions and 0 deletions.
    8 changes: 8 additions & 0 deletions integration-test-http-trigger.cs
    Original file line number Diff line number Diff line change
    @@ -14,10 +14,14 @@ public void Init()
    [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);
    }
    }
    @@ -26,10 +30,14 @@ public async Task Given_Url_When_Invoked_Then_Trigger_Should_Return_Healthy()
    [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);
    }
    }
  8. justinyoo created this gist Jul 21, 2019.
    32 changes: 32 additions & 0 deletions health-check-function.cs
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,32 @@
    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;
    }
    }
    15 changes: 15 additions & 0 deletions health-check-http-trigger.cs
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,15 @@
    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);
    }
    }
    1 change: 1 addition & 0 deletions install-mountebank.sh
    Original file line number Diff line number Diff line change
    @@ -0,0 +1 @@
    npm install -g mountebank
    1 change: 1 addition & 0 deletions integration-test-dotnet-cli.sh
    Original file line number Diff line number Diff line change
    @@ -0,0 +1 @@
    dotnet test [Test_Project_Name].csproj -c Release --filter:"TestCategory=Integration"
    36 changes: 36 additions & 0 deletions integration-test-http-trigger.cs
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,36 @@
    [TestClass]
    public class HealthCheckHttpTriggerTests
    {
    private const string CategoryIntegration = "Integration";
    private ServerFixture _fixture;

    [TestInitialize]
    public void Init()
    {
    this._fixture = new MountebankServerFixture();
    }

    [TestMethod]
    [TestCategory(CategoryIntegration)]
    public async Task Given_Url_When_Invoked_Then_Trigger_Should_Return_Healthy()
    {
    var uri = this._fixture.GetHealthCheckUrl();
    using (var http = new HttpClient())
    using (var res = await http.GetAsync(uri))
    {
    res.StatusCode.Should().Be(HttpStatusCode.OK);
    }
    }

    [TestMethod]
    [TestCategory(CategoryIntegration)]
    public async Task Given_Url_When_Invoked_Then_Trigger_Should_Return_Unhealthy()
    {
    var uri = this._fixture.GetHealthCheckUrl(HttpStatusCode.InternalServerError);
    using (var http = new HttpClient())
    using (var res = await http.GetAsync(uri))
    {
    res.StatusCode.Should().Be(HttpStatusCode.InternalServerError);
    }
    }
    }
    24 changes: 24 additions & 0 deletions mountebank-server-fixture.cs
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,24 @@
    public class MountebankServerFixture
    {
    private readonly MountebankClient _client;

    public MountebankServerFixture()
    {
    this._client = new MountebankClient();
    }

    public override 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";
    }
    }
    1 change: 1 addition & 0 deletions run-mountebank.sh
    Original file line number Diff line number Diff line change
    @@ -0,0 +1 @@
    mb
    2 changes: 2 additions & 0 deletions run-services-1.sh
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,2 @@
    start /b mb --noLogFile
    start /b func host start --csharp
    5 changes: 5 additions & 0 deletions run-services-2.sh
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,5 @@
    # Console #1
    mb --noLogFile

    # Console #2
    func host start --csharp
    1 change: 1 addition & 0 deletions unit-test-dotnet-cli.sh
    Original file line number Diff line number Diff line change
    @@ -0,0 +1 @@
    dotnet test [Test_Project_Name].csproj -c Release
    24 changes: 24 additions & 0 deletions unit-test-function.cs
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,24 @@
    [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);
    }
    24 changes: 24 additions & 0 deletions unit-test-http-trigger.cs
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,24 @@
    [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);
    }