diff --git a/.github/CHANGELOG.md b/.github/CHANGELOG.md index 7d93f1f..17ae989 100644 --- a/.github/CHANGELOG.md +++ b/.github/CHANGELOG.md @@ -1,5 +1,19 @@ # Change Log +## v2.5.0 - June 2022 + +- updated condition to run webhost without `--prometheus` +- removed unit tests that referenced external sources +- fixed max duration bug + +## v2.4.0 - March 2022 + +- fixed thread sync bug +- added /readyz endpoint +- updated packages +- bumped version +- Retail Edge customer feedback + ## v2.3.1 - Dec 2021 - minor bug fixes diff --git a/.github/workflows/codeql.yaml b/.github/workflows/codeql.yaml index f988795..55e5ee7 100644 --- a/.github/workflows/codeql.yaml +++ b/.github/workflows/codeql.yaml @@ -1,6 +1,18 @@ -name: "CodeQL" +name: CodeQL on: + workflow_dispatch: + + push: + branches: + - main + + paths-ignore: + - '.devcontainer/**' + - 'docs/**' + - '**.md' + - '**.json' + pull_request: branches: [main] @@ -43,16 +55,15 @@ jobs: with: languages: ${{ matrix.language }} + - name: Setup .NET + uses: actions/setup-dotnet@v1 + with: + dotnet-version: '6.0' + include-prerelease: True + # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). - # Autobuild doesn't build correctly, so we run a manual build - name: Autobuild - # uses: github/codeql-action/autobuild@v1 - run: | - # install dotnet 6 - sudo apt-get -y install --no-install-recommends dotnet-sdk-6.0 - - cd src - dotnet build + uses: github/codeql-action/autobuild@v2 - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v1 + uses: github/codeql-action/analyze@v2 diff --git a/.github/workflows/dockerCI.yaml b/.github/workflows/dockerCI.yaml index 8e8b31a..f62b79b 100644 --- a/.github/workflows/dockerCI.yaml +++ b/.github/workflows/dockerCI.yaml @@ -1,10 +1,8 @@ -name: Docker Image Build +name: DockerBuild on: - schedule: - # Run on Sunday at 6:00 AM UTC - - cron: "0 6 * * 0" - + workflow_dispatch: + push: branches: - main diff --git a/README.md b/README.md index 4a3ad99..f7c5c55 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,8 @@ # Web Validate - A web request validation tool ![License](https://img.shields.io/badge/license-MIT-green.svg) -![Docker Image Build](https://github.com/microsoft/webvalidate/workflows/Docker%20Image%20Build/badge.svg) +![CodeQL Build](https://github.com/microsoft/webvalidate/workflows/CodeQL/badge.svg) +![Docker Build](https://github.com/microsoft/webvalidate/workflows/DockerBuild/badge.svg) Web Validate (WebV) is a web request validation tool that we use to run end-to-end tests and long-running performance and availability tests. diff --git a/src/app/Program.cs b/src/app/Program.cs index 3397c37..ff249b0 100644 --- a/src/app/Program.cs +++ b/src/app/Program.cs @@ -127,28 +127,25 @@ public static async Task Run(Config config, ParseResult parseResult) { IHost host = null; - if (config.Prometheus) - { - // build and run the web host - host = BuildWebHost(config.Port); - Task t = host.StartAsync(TokenSource.Token); - - if (t.IsFaulted) - { - // stop and dispose the web host - await host.StopAsync(TimeSpan.FromMilliseconds(100)).ConfigureAwait(false); - host.Dispose(); - host = null; + // build and run the web host + host = BuildWebHost(config.Port); + Task t = host.StartAsync(TokenSource.Token); - Console.WriteLine("\n\nUnabled to start web server\n"); + if (t.IsFaulted) + { + // stop and dispose the web host + await host.StopAsync(TimeSpan.FromMilliseconds(100)).ConfigureAwait(false); + host.Dispose(); + host = null; - if (t.Exception != null) - { - Console.WriteLine(t.Exception.Message); - } + Console.WriteLine("\n\nUnabled to start web server\n"); - return -1; + if (t.Exception != null) + { + Console.WriteLine(t.Exception.Message); } + + return -1; } // run in a loop diff --git a/src/app/WebValidation/Main.cs b/src/app/WebValidation/Main.cs index 849b05e..037cb42 100644 --- a/src/app/WebValidation/Main.cs +++ b/src/app/WebValidation/Main.cs @@ -452,7 +452,7 @@ public async Task ExecuteRequest(HttpClient client, string server, Requ double duration = Math.Round(DateTime.UtcNow.Subtract(dt).TotalMilliseconds, 0); // validate the response - valid = ResponseValidator.Validate(request, resp, body); + valid = ResponseValidator.Validate(request, resp, body, duration); // check the performance perfLog = CreatePerfLog(server, request, valid, duration, (long)resp.Content.Headers.ContentLength, (int)resp.StatusCode, cv.Value); diff --git a/src/app/WebValidation/Model/Validation.cs b/src/app/WebValidation/Model/Validation.cs index 436f896..1f6fd97 100644 --- a/src/app/WebValidation/Model/Validation.cs +++ b/src/app/WebValidation/Model/Validation.cs @@ -44,7 +44,7 @@ public class Validation /// gets or sets the maximum ms for the request /// default: null (ignore) /// - public int? MaxMilliseconds { get; set; } + public int? MaxMilliSeconds { get; set; } /// /// gets or sets the list of strings that must be in the response diff --git a/src/app/WebValidation/Validators/ParameterValidator.cs b/src/app/WebValidation/Validators/ParameterValidator.cs index e298169..624f8a1 100644 --- a/src/app/WebValidation/Validators/ParameterValidator.cs +++ b/src/app/WebValidation/Validators/ParameterValidator.cs @@ -81,7 +81,7 @@ public static ValidationResult Validate(Validation v) res.Add(ValidateLength(v)); // validate MaxMilliSeconds - if (v.MaxMilliseconds != null && v.MaxMilliseconds <= 0) + if (v.MaxMilliSeconds != null && v.MaxMilliSeconds <= 0) { res.Failed = true; res.ValidationErrors.Add("maxMilliseconds: maxMilliseconds cannot be less than zero"); diff --git a/src/app/WebValidation/Validators/ResponseValidator.cs b/src/app/WebValidation/Validators/ResponseValidator.cs index 5af67e8..2031be4 100644 --- a/src/app/WebValidation/Validators/ResponseValidator.cs +++ b/src/app/WebValidation/Validators/ResponseValidator.cs @@ -22,8 +22,9 @@ public static class ResponseValidator /// Request /// HttpResponseMessage /// response body + /// request duration /// ValidationResult - public static ValidationResult Validate(Request r, HttpResponseMessage response, string body) + public static ValidationResult Validate(Request r, HttpResponseMessage response, string body, double duration) { ValidationResult result = new (); @@ -48,6 +49,15 @@ public static ValidationResult Validate(Request r, HttpResponseMessage response, return result; } + // validate max duration + if (r.Validation.MaxMilliSeconds > 0) + { + if (duration > r.Validation.MaxMilliSeconds) + { + result.ValidationErrors.Add($"duration: {duration}ms exceeded max value {r.Validation.MaxMilliSeconds}ms"); + } + } + // redirects don't have body or headers if ((int)response.StatusCode >= 300 && (int)response.StatusCode <= 399) { diff --git a/src/app/duration.json b/src/app/duration.json new file mode 100644 index 0000000..88dce2d --- /dev/null +++ b/src/app/duration.json @@ -0,0 +1,10 @@ +// test command: +// dotnet run -- --verbose-errors -s https://bing.com -f duration.json +// validation errors are expected + +{ + "requests": + [ + {"path":"/","perfTarget":{"category":"Static"},"validation":{"maxMilliSeconds":1, "statusCode":301}} + ] +} diff --git a/src/app/github.json b/src/app/github.json index eec8943..0f5ea26 100644 --- a/src/app/github.json +++ b/src/app/github.json @@ -1,3 +1,6 @@ +// test command +// dotnet run -- --verbose-errors -s https://api.github.com -f github.json + { "requests": [ diff --git a/src/app/msft.json b/src/app/msft.json index 99a9ff1..3f316b6 100644 --- a/src/app/msft.json +++ b/src/app/msft.json @@ -1,8 +1,11 @@ +// test command +// dotnet run -- --verbose-errors -s https://www.microsoft.com -f msft.json + { "requests": [ {"path":"/","testName": "mytest1", "perfTarget":{"category":"Static"},"validation":{"statusCode":302}}, - {"path":"/en-us","perfTarget":{"category":"Static"},"validation":{"contentType":"text/html","contains":["Microsoft - Official Home Page","Microsoft – Cloud, Computers, Apps"," net6.0 CSE.WebValidate - 2.4.0 + 2.5.0 $([System.DateTime]::UtcNow.ToString(`MMdd-HHmm`)) Microsoft Corporation Copyright (c) Microsoft Corporation. All rights reserved. diff --git a/src/tests/TestApp.cs b/src/tests/TestApp.cs index 9d7c8a5..ff73deb 100644 --- a/src/tests/TestApp.cs +++ b/src/tests/TestApp.cs @@ -166,27 +166,5 @@ private static Config BuildConfig(string server) MaxErrors = 10, }; } - - [Fact] - public async Task MsftTest() - { - Config cfg = BuildConfig("https://www.microsoft.com"); - cfg.Files.Add("msft.json"); - - // load and validate all of our test files - WebV wv = new (cfg); - Assert.Equal(0, await wv.RunOnce(cfg, new System.Threading.CancellationToken()).ConfigureAwait(false)); - } - - [Fact] - public async Task GithubTest() - { - Config cfg = BuildConfig("https://api.github.com"); - cfg.Files.Add("github.json"); - - // load and validate all of our test files - WebV wv = new (cfg); - Assert.Equal(0, await wv.RunOnce(cfg, new System.Threading.CancellationToken()).ConfigureAwait(false)); - } } } diff --git a/src/tests/TestCommonValidator.cs b/src/tests/TestCommonValidator.cs index fb9fb72..b3e1748 100644 --- a/src/tests/TestCommonValidator.cs +++ b/src/tests/TestCommonValidator.cs @@ -53,7 +53,7 @@ public void CommonBoundariesTest() v.StatusCode = 10; // > 0 - v.MaxMilliseconds = 0; + v.MaxMilliSeconds = 0; // ! isnullorempty v.ExactMatch = string.Empty; @@ -108,14 +108,14 @@ public void ResponseNullTest() { Request r = new (); - Assert.False(ResponseValidator.Validate(r, null, string.Empty).Failed); + Assert.False(ResponseValidator.Validate(r, null, string.Empty, 0).Failed); r.Validation = new Validation(); - Assert.True(ResponseValidator.Validate(r, null, "this is a test").Failed); + Assert.True(ResponseValidator.Validate(r, null, "this is a test", 0).Failed); using System.Net.Http.HttpResponseMessage resp = new (System.Net.HttpStatusCode.NotFound); - Assert.True(ResponseValidator.Validate(r, resp, "this is a test").Failed); + Assert.True(ResponseValidator.Validate(r, resp, "this is a test", 0).Failed); Assert.True(ResponseValidator.ValidateStatusCode(400, 200).Failed); }