Replace HttpListener Link Header Tests with WebListener (#5806)

- Add Link controller to WebListener
- Replace HttpListener Link tests with WebListener
- Update WebListener Documentation
- Enable cross-platform multiple Link header tests
This commit is contained in:
Mark Kraus
2018-01-16 09:23:22 -08:00
committed by Dongbo Wang
parent c8ccb376b4
commit 3f9564e665
6 changed files with 215 additions and 42 deletions
@@ -712,33 +712,31 @@ Describe "Invoke-WebRequest tests" -Tags "Feature" {
}
It "Validate Invoke-WebRequest returns valid RelationLink property with absolute uris if Link Header is present" {
$command = "Invoke-WebRequest -Uri 'http://localhost:8080/PowerShell?test=linkheader&maxlinks=5'"
$result = ExecuteWebCommand -command $command
$result.Output.RelationLink.Count | Should BeExactly 2
$result.Output.RelationLink["next"] | Should BeExactly "http://localhost:8080/PowerShell?test=linkheader&maxlinks=5&linknumber=2"
$result.Output.RelationLink["last"] | Should BeExactly "http://localhost:8080/PowerShell?test=linkheader&maxlinks=5&linknumber=5"
}
# Test pending support for multiple header capable server on Linux/macOS see issue #4639
It "Validate Invoke-WebRequest returns valid RelationLink property with absolute uris if Multiple Link Headers are present" -Pending:$(!$IsWindows) {
$Query = @{
body = "ok"
contenttype = 'text/plain'
headers = @{
Link = @(
'<http://localhost:8080/PowerShell?test=linkheader&maxlinks=5&linknumber=1>; rel="self"'
'<http://localhost:8080/PowerShell?test=linkheader&maxlinks=5&linknumber=2>; rel="next"'
'<http://localhost:8080/PowerShell?test=linkheader&maxlinks=5&linknumber=5>; rel="last"'
)
} | ConvertTo-Json -Compress
}
$Uri = Get-WebListenerUrl -Test 'Response' -Query $Query
$uri = Get-WebListenerUrl -Test 'Link' -Query @{maxlinks = 5; linknumber = 2}
$command = "Invoke-WebRequest -Uri '$uri'"
$result = ExecuteWebCommand -command $command
$result.Output.RelationLink.Count | Should BeExactly 3
$result.Output.RelationLink["self"] | Should BeExactly "http://localhost:8080/PowerShell?test=linkheader&maxlinks=5&linknumber=1"
$result.Output.RelationLink["next"] | Should BeExactly "http://localhost:8080/PowerShell?test=linkheader&maxlinks=5&linknumber=2"
$result.Output.RelationLink["last"] | Should BeExactly "http://localhost:8080/PowerShell?test=linkheader&maxlinks=5&linknumber=5"
$result.Output.RelationLink.Count | Should BeExactly 5
$baseUri = Get-WebListenerUrl -Test 'Link'
$result.Output.RelationLink["next"] | Should BeExactly "${baseUri}?maxlinks=5&linknumber=3&type=default"
$result.Output.RelationLink["last"] | Should BeExactly "${baseUri}?maxlinks=5&linknumber=5&type=default"
$result.Output.RelationLink["prev"] | Should BeExactly "${baseUri}?maxlinks=5&linknumber=1&type=default"
$result.Output.RelationLink["first"] | Should BeExactly "${baseUri}?maxlinks=5&linknumber=1&type=default"
$result.Output.RelationLink["self"] | Should BeExactly "${baseUri}?maxlinks=5&linknumber=2&type=default"
}
It "Validate Invoke-WebRequest returns valid RelationLink property with absolute uris if Multiple Link Headers are present" {
$uri = Get-WebListenerUrl -Test 'Link' -Query @{maxlinks = 5; linknumber = 2; type = 'multiple'}
$command = "Invoke-WebRequest -Uri '$uri'"
$result = ExecuteWebCommand -command $command
$result.Output.RelationLink.Count | Should BeExactly 5
$baseUri = Get-WebListenerUrl -Test 'Link'
$result.Output.RelationLink["next"] | Should BeExactly "${baseUri}?maxlinks=5&linknumber=3&type=multiple"
$result.Output.RelationLink["last"] | Should BeExactly "${baseUri}?maxlinks=5&linknumber=5&type=multiple"
$result.Output.RelationLink["prev"] | Should BeExactly "${baseUri}?maxlinks=5&linknumber=1&type=multiple"
$result.Output.RelationLink["first"] | Should BeExactly "${baseUri}?maxlinks=5&linknumber=1&type=multiple"
$result.Output.RelationLink["self"] | Should BeExactly "${baseUri}?maxlinks=5&linknumber=2&type=multiple"
}
It "Validate Invoke-WebRequest quietly ignores invalid Link Headers in RelationLink property: <type>" -TestCases @(
@@ -747,10 +745,15 @@ Describe "Invoke-WebRequest tests" -Tags "Feature" {
@{ type = "noRel" }
) {
param($type)
$command = "Invoke-WebRequest -Uri 'http://localhost:8080/PowerShell?test=linkheader&type=$type'"
$uri = Get-WebListenerUrl -Test 'Link' -Query @{type = $type}
$command = "Invoke-WebRequest -Uri '$uri'"
$result = ExecuteWebCommand -command $command
$result.Output.RelationLink.Count | Should BeExactly 1
$result.Output.RelationLink["last"] | Should BeExactly "http://localhost:8080/PowerShell?test=linkheader&maxlinks=3&linknumber=3"
$result.Output.RelationLink.Count | Should BeExactly 3
$baseUri = Get-WebListenerUrl -Test 'Link'
$result.Output.RelationLink["last"] | Should BeExactly "${baseUri}?maxlinks=3&linknumber=3&type=${type}"
$result.Output.RelationLink["first"] | Should BeExactly "${baseUri}?maxlinks=3&linknumber=1&type=${type}"
$result.Output.RelationLink["self"] | Should BeExactly "${baseUri}?maxlinks=3&linknumber=1&type=${type}"
}
#region Redirect tests
@@ -1030,9 +1033,7 @@ Describe "Invoke-WebRequest tests" -Tags "Feature" {
$result.Output.RawContent | Should Match ([regex]::Escape('Content-Length: 2'))
}
# Test pending due to limitation on Linux/macOS
# https://github.com/PowerShell/PowerShell/pull/4640
It "Verifies Invoke-WebRequest Supports Multiple response headers with same name" -Pending:$(!$IsWindows) {
It "Verifies Invoke-WebRequest Supports Multiple response headers with same name" {
$query = @{
contenttype = 'text/plain'
body = 'OK'
@@ -1723,23 +1724,23 @@ Describe "Invoke-RestMethod tests" -Tags "Feature" {
It "Validate Invoke-RestMethod -FollowRelLink correctly follows all the available relation links" {
$maxLinks = 5
$command = "Invoke-RestMethod -Uri 'http://localhost:8081/PowerShell?test=linkheader&maxlinks=$maxlinks' -FollowRelLink"
$uri = Get-WebListenerUrl -Test 'Link' -Query @{maxlinks = $maxLinks}
$command = "Invoke-RestMethod -Uri '$uri' -FollowRelLink"
$result = ExecuteWebCommand -command $command
$result.Output.output.Count | Should BeExactly $maxLinks
1..$maxLinks | ForEach-Object { $result.Output.output[$_ - 1] | Should BeExactly $_ }
$result.Output.Count | Should BeExactly $maxLinks
1..$maxLinks | ForEach-Object { $result.Output[$_ - 1].linknumber | Should BeExactly $_ }
}
It "Validate Invoke-RestMethod -FollowRelLink correctly limits to -MaximumRelLink" {
$maxLinks = 10
$maxLinksToFollow = 6
$command = "Invoke-RestMethod -Uri 'http://localhost:8081/PowerShell?test=linkheader&maxlinks=$maxlinks' -FollowRelLink -MaximumFollowRelLink $maxLinksToFollow"
$uri = Get-WebListenerUrl -Test 'Link' -Query @{maxlinks = $maxLinks}
$command = "Invoke-RestMethod -Uri '$uri' -FollowRelLink -MaximumFollowRelLink $maxLinksToFollow"
$result = ExecuteWebCommand -command $command
$result.Output.output.Count | Should BeExactly $maxLinksToFollow
1..$maxLinksToFollow | ForEach-Object { $result.Output.output[$_ - 1] | Should BeExactly $_ }
$result.Output.Count | Should BeExactly $maxLinksToFollow
1..$maxLinksToFollow | ForEach-Object { $result.Output[$_ - 1].linknumber | Should BeExactly $_ }
}
It "Validate Invoke-RestMethod quietly ignores invalid Link Headers if -FollowRelLink is specified: <type>" -TestCases @(
@@ -1748,9 +1749,10 @@ Describe "Invoke-RestMethod tests" -Tags "Feature" {
@{ type = "noRel" }
) {
param($type)
$command = "Invoke-RestMethod -Uri 'http://localhost:8081/PowerShell?test=linkheader&type=$type' -FollowRelLink"
$uri = Get-WebListenerUrl -Test 'Link' -Query @{type = $type}
$command = "Invoke-RestMethod -Uri '$uri' -FollowRelLink"
$result = ExecuteWebCommand -command $command
$result.Output.output | Should BeExactly 1
$result.Output.linknumber | Should BeExactly 1
}
#region Redirect tests
@@ -135,6 +135,7 @@ function Get-WebListenerUrl {
'Encoding',
'Get',
'Home',
'Link',
'Multipart',
'Patch',
'Post',
+6
View File
@@ -6,6 +6,12 @@ namespace mvc.Controllers
{
public const string HeaderSeparator = ", ";
public const string ApplicationJson = "application/json";
public const string LinkUriTemplate = "<{0}?maxlinks={1}&linknumber={2}&type={3}>; rel=\"{4}\"";
public const string MalformedUrlLinkHeader = "{url}; foo";
public const string NoRelLinkHeader = "<url>; foo=\"bar\"";
public const string NoUrlLinkHeader = "<>; rel=\"next\"";
}
internal static class StatusCodes
@@ -0,0 +1,102 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text.RegularExpressions;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Http.Extensions;
using Microsoft.Extensions.Primitives;
using mvc.Models;
namespace mvc.Controllers
{
public class LinkController : Controller
{
public JsonResult Index()
{
if (!Request.Query.TryGetValue("maxlinks", out StringValues maxLinksSV) || !Int32.TryParse(maxLinksSV.FirstOrDefault(), out int maxLinks) || maxLinks < 1)
{
maxLinks = 3;
}
if (!Request.Query.TryGetValue("linknumber", out StringValues linkNumberSV) || !Int32.TryParse(linkNumberSV.FirstOrDefault(), out int linkNumber) || linkNumber < 1)
{
linkNumber = 1;
}
string baseUri = Regex.Replace(UriHelper.GetDisplayUrl(Request), "\\?.*", String.Empty);
string type = Request.Query.TryGetValue("type", out StringValues typeSV) ? typeSV.FirstOrDefault() : "default";
var linkList = new List<String>();
if (maxLinks > 1 && linkNumber > 1)
{
linkList.Add(GetLink(baseUri: baseUri, maxLinks: maxLinks, linkNumber: linkNumber - 1, type: type, rel: "prev"));
}
linkList.Add(GetLink(baseUri: baseUri, maxLinks: maxLinks, linkNumber: maxLinks, type: type, rel: "last"));
linkList.Add(GetLink(baseUri: baseUri, maxLinks: maxLinks, linkNumber: 1, type: type, rel: "first"));
linkList.Add(GetLink(baseUri: baseUri, maxLinks: maxLinks, linkNumber: linkNumber, type: type, rel: "self"));
bool sendMultipleHeaders = false;
bool skipNextLink = false;
switch (type.ToUpper())
{
case "NOURL":
linkList.Add(Constants.NoUrlLinkHeader);
skipNextLink = true;
break;
case "MALFORMED":
linkList.Add(Constants.MalformedUrlLinkHeader);
skipNextLink = true;
break;
case "NOREL":
linkList.Add(Constants.NoRelLinkHeader);
skipNextLink = true;
break;
case "MULTIPLE":
sendMultipleHeaders = true;
break;
default:
break;
}
if (!skipNextLink && maxLinks > 1 && linkNumber < maxLinks)
{
linkList.Add(GetLink(baseUri: baseUri, maxLinks: maxLinks, linkNumber: linkNumber + 1, type: type, rel: "next"));
}
StringValues linkHeader;
if (sendMultipleHeaders)
{
linkHeader = linkList.ToArray();
}
else
{
linkHeader = String.Join(",", linkList);
}
Response.Headers.Add("Link", linkHeader);
// Generate /Get/ result and append linknumber, maxlinks, and type
var getController = new GetController();
getController.ControllerContext = this.ControllerContext;
var result = getController.Index();
var output = result.Value as Hashtable;
output.Add("linknumber", linkNumber);
output.Add("maxlinks", maxLinks);
output.Add("type", type.FirstOrDefault());
return result;
}
public IActionResult Error()
{
return View(new ErrorViewModel { RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier });
}
private string GetLink(string baseUri, int maxLinks, int linkNumber, string type, string rel)
{
return String.Format(Constants.LinkUriTemplate, baseUri, maxLinks, linkNumber, type, rel);
}
}
}
+61
View File
@@ -265,6 +265,67 @@ Invoke-RestMethod -Uri $uri -Body @{TestField = 'TestValue'}
}
```
### /Link/
Returns Link response headers to test paginated results. The endpoint accepts 3 query items:
* `linknumber` - The current link number. This determines the current page. If not supplied or less than 1, this will be set to 1.
* `maxlinks` - The maximum number of links. This determines the last page. If not supplied or less than 1, this will be set to 3.
* `type` - The type of link to return. When not supplied or not in the list below, `default` will be used.
* `default` - Does not return any special test links and returns `next` link if one is available.
* `norel` - Returns a Link header that does not include the `rel=` portion. Suppresses `next` link.
* `nourl` - Returns a Link header that does not include the URI portion. Suppresses `next` link.
* `malformed` - Returns a malformed Link header. Suppresses `next` link.
* `multiple` - Returns multiple Link headers instead of a single Link header and returns `next` link if one is available.
The body will contain the same results as `/Get/` with the addition of the `type`, `linknumber`, and `maxlinks` for the current page.
```powershell
$Query = @{
linknumber = 1
maxlinks = 3
type = 'default'
}
$Uri = Get-WebListenerUrl -Test 'Link' -Query $Query
Invoke-RestMethod -Uri $uri -FollowRelLink -MaximumFollowRelLink 1
```
Headers:
```none
HTTP/1.1 200 OK
Date: Sat, 06 Jan 2018 14:27:36 GMT
Content-Type: application/json; charset=utf-8
Server: Kestrel
Transfer-Encoding: chunked
Link: <http://localhost:8083/Link/?maxlinks=3&linknumber=3>; rel="last",<http://localhost:8083/Link/?maxlinks=3&linknumber=1>; rel="first",<http://localhost:8083/Link/?maxlinks=3&linknumber=1>; rel="self",<http://localhost:8083/Link/?maxlinks=3&linknumber=2>; rel="next"
```
Body:
```json
{
"type": "default",
"url": "http://localhost:8083/Link/?maxlinks=3&linknumber=1&type=default",
"maxlinks": 3,
"linknumber": 1,
"headers": {
"User-Agent": "insomnia/5.12.4",
"Accept": "*/*",
"Content-Length": "0",
"Host": "localhost:8083",
"Content-Type": "application/json"
},
"args": {
"linknumber": "1",
"maxlinks": "3",
"type": "default"
},
"origin": "127.0.0.1",
"method": "GET"
}
```
### /Multipart/
#### GET
@@ -11,7 +11,8 @@
<li>/Delete/ - returns data from a DELETE request</li>
<li><a href="/Encoding/Utf8/">/Encoding/Utf8/</a> - Returns page containing UTF-8 data.</li>
<li><a href="/Get/">/Get/</a> - Emulates functionality of https://httpbin.org/get by returning GET headers, Arguments, and Request URL</li>
<li><a href="/Multipart/">/Multipart/</a> - Multipart/form-data submission testing</li>
<li><a href="/Get/">/Get/</a> - Emulates functionality of https://httpbin.org/get by returning GET headers, Arguments, and Request URL</li>
<li><a href="/Link/?linknumber=1&maxlinks=3&type=default">/Link/</a> - Link Header (pagination) testing</li>
<li>/Patch/ - returns data from a PATCH request</li>
<li>/Post/ - returns data from a POST request</li>
<li>/Put/ - returns data from a PUT request</li>