Skip to content

Commit

Permalink
Fix single header assertion with no values (#129)
Browse files Browse the repository at this point in the history
* Fix single header assertion with no values

* Bump version
  • Loading branch information
Hawxy authored Dec 2, 2022
1 parent 74051b2 commit 44b04a2
Show file tree
Hide file tree
Showing 10 changed files with 143 additions and 20 deletions.
2 changes: 1 addition & 1 deletion docs/guide/index.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
# Guide

Just here for Algolia
Just here for Algolia
19 changes: 11 additions & 8 deletions docs/scenarios/headers.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ public async Task setting_request_headers(IAlbaHost system)
});
}
```
<sup><a href='https://github.com/JasperFx/alba/blob/master/src/Alba.Testing/Samples/Headers.cs#L29-L38' title='Snippet source file'>snippet source</a> | <a href='#snippet-sample_setting_request_headers' title='Start of snippet'>anchor</a></sup>
<sup><a href='https://github.com/JasperFx/alba/blob/master/src/Alba.Testing/Samples/Headers.cs#L30-L39' title='Snippet source file'>snippet source</a> | <a href='#snippet-sample_setting_request_headers' title='Start of snippet'>anchor</a></sup>
<!-- endSnippet -->

There are also some specific helpers for very common [content negotiation-related](https://en.wikipedia.org/wiki/Content_negotiation) headers:
Expand All @@ -47,7 +47,7 @@ public async Task conneg_helpers(IAlbaHost system)
});
}
```
<sup><a href='https://github.com/JasperFx/alba/blob/master/src/Alba.Testing/Samples/Headers.cs#L8-L27' title='Snippet source file'>snippet source</a> | <a href='#snippet-sample_conneg_helpers' title='Start of snippet'>anchor</a></sup>
<sup><a href='https://github.com/JasperFx/alba/blob/master/src/Alba.Testing/Samples/Headers.cs#L9-L28' title='Snippet source file'>snippet source</a> | <a href='#snippet-sample_conneg_helpers' title='Start of snippet'>anchor</a></sup>
<!-- endSnippet -->

**Do add extension methods off of the Alba `Scenario` class for common operations in your tests to remove
Expand All @@ -65,26 +65,29 @@ public async Task asserting_on_header_values(IAlbaHost system)
await system.Scenario(_ =>
{
// Assert that there is one and only one value equal to "150"
_.Header("content-length").SingleValueShouldEqual("150");
_.Header(HeaderNames.ContentLength).SingleValueShouldEqual("150");

// Assert that there is no value for this response header
_.Header("connection").ShouldNotBeWritten();
_.Header(HeaderNames.Connection).ShouldNotBeWritten();

// Only write one value for this header
_.Header("set-cookie").ShouldHaveOneNonNullValue();
_.Header(HeaderNames.SetCookie).ShouldHaveOneNonNullValue();

// Assert that the header has any values
_.Header(HeaderNames.ETag).ShouldHaveValues();

// Assert that the header has the given values
_.Header("www-authenticate").ShouldHaveValues("NTLM", "Negotiate");
_.Header(HeaderNames.WWWAuthenticate).ShouldHaveValues("NTLM", "Negotiate");

// Assert that the header matches a regular expression
_.Header("location").SingleValueShouldMatch(new Regex(@"^/items/\d*$"));
_.Header(HeaderNames.Location).SingleValueShouldMatch(new Regex(@"^/items/\d*$"));

// Check the content-type header
_.ContentTypeShouldBe("text/json");
});
}
```
<sup><a href='https://github.com/JasperFx/alba/blob/master/src/Alba.Testing/Samples/Headers.cs#L41-L65' title='Snippet source file'>snippet source</a> | <a href='#snippet-sample_asserting_on_header_values' title='Start of snippet'>anchor</a></sup>
<sup><a href='https://github.com/JasperFx/alba/blob/master/src/Alba.Testing/Samples/Headers.cs#L42-L69' title='Snippet source file'>snippet source</a> | <a href='#snippet-sample_asserting_on_header_values' title='Start of snippet'>anchor</a></sup>
<!-- endSnippet -->

You do also have the ability to just interrogate the `HttpContext.Response` in your unit test methods for
Expand Down
2 changes: 1 addition & 1 deletion docs/scenarios/index.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
# Scenarios

Just here for Algolia
Just here for Algolia
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System.Threading.Tasks;
using System;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Shouldly;
using Xunit;
Expand Down Expand Up @@ -244,5 +245,84 @@ public async Task multiple_header_values_no_values()
ex.Message.ShouldContain(
"Expected header values of 'Foo'='Bar', but no values were found on the response.");
}

[Fact]
public async Task multiple_header_values_bad_argument()
{
router.Handlers["/one"] = c => Task.CompletedTask;

var ex = await Exception<ArgumentException>.ShouldBeThrownBy(() =>
{
return host.Scenario(x =>
{
x.Post.Url("/one");
x.Header("Foo").ShouldHaveValues(Array.Empty<string>());
});
});

ex.Message.ShouldContain(
"Expected values must contain at least one value");
}

[Fact]
public Task header_values_exist_happy_path()
{
router.Handlers["/one"] = c =>
{
c.Response.Headers.Append("Foo", "Anything");
c.Response.Headers.Append("Foo", "Bar");
return Task.CompletedTask;
};

return host.Scenario(x =>
{
x.Post.Url("/one");
x.Header("Foo").ShouldHaveValues();
});
}

[Fact]
public async Task header_values_exist_empty_value()
{
router.Handlers["/one"] = c =>
{
c.Response.Headers.Append("Foo", "");
return Task.CompletedTask;
};

var ex = await Exception<ScenarioAssertionException>.ShouldBeThrownBy(() =>
{
return host.Scenario(x =>
{
x.Post.Url("/one");
x.Header("Foo").ShouldHaveValues();
});
});

ex.Message.ShouldContain(
"Expected header 'Foo' to be present but no values were found on the response."); ;
}


[Fact]
public async Task header_values_exist_no_values()
{
router.Handlers["/one"] = c => Task.CompletedTask;

var ex = await Exception<ScenarioAssertionException>.ShouldBeThrownBy(() =>
{
return host.Scenario(x =>
{
x.Post.Url("/one");
x.Header("Foo").ShouldHaveValues();
});
});

ex.Message.ShouldContain(
"Expected header 'Foo' to be present but no values were found on the response.");
}



}
}
1 change: 1 addition & 0 deletions src/Alba.Testing/Assertions/NoHeaderValueAssertionTests.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
using Alba.Assertions;
using Microsoft.AspNetCore.Http;
using Xunit;

Expand Down
14 changes: 9 additions & 5 deletions src/Alba.Testing/Samples/Headers.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using Microsoft.Net.Http.Headers;

namespace Alba.Testing.Samples
{
Expand Down Expand Up @@ -44,19 +45,22 @@ public async Task asserting_on_header_values(IAlbaHost system)
await system.Scenario(_ =>
{
// Assert that there is one and only one value equal to "150"
_.Header("content-length").SingleValueShouldEqual("150");
_.Header(HeaderNames.ContentLength).SingleValueShouldEqual("150");

// Assert that there is no value for this response header
_.Header("connection").ShouldNotBeWritten();
_.Header(HeaderNames.Connection).ShouldNotBeWritten();

// Only write one value for this header
_.Header("set-cookie").ShouldHaveOneNonNullValue();
_.Header(HeaderNames.SetCookie).ShouldHaveOneNonNullValue();

// Assert that the header has any values
_.Header(HeaderNames.ETag).ShouldHaveValues();

// Assert that the header has the given values
_.Header("www-authenticate").ShouldHaveValues("NTLM", "Negotiate");
_.Header(HeaderNames.WWWAuthenticate).ShouldHaveValues("NTLM", "Negotiate");

// Assert that the header matches a regular expression
_.Header("location").SingleValueShouldMatch(new Regex(@"^/items/\d*$"));
_.Header(HeaderNames.Location).SingleValueShouldMatch(new Regex(@"^/items/\d*$"));

// Check the content-type header
_.ContentTypeShouldBe("text/json");
Expand Down
2 changes: 1 addition & 1 deletion src/Alba/Alba.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
<PropertyGroup>
<Description>Supercharged integration testing for ASP.NET Core HTTP endpoints</Description>
<AssemblyTitle>Alba</AssemblyTitle>
<Version>7.2.0</Version>
<Version>7.2.1</Version>
<TargetFrameworks>net6.0;net7.0</TargetFrameworks>
<AssemblyName>Alba</AssemblyName>
<PackageId>Alba</PackageId>
Expand Down
24 changes: 24 additions & 0 deletions src/Alba/Assertions/HeaderExistsAssertion.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
using Microsoft.AspNetCore.Http;

namespace Alba.Assertions;

internal class HeaderExistsAssertion : IScenarioAssertion
{
private readonly string _headerKey;

public HeaderExistsAssertion(string headerKey)
{
_headerKey = headerKey;
}

public void Assert(Scenario scenario, HttpContext context, ScenarioAssertionException ex)
{
var values = context.Response.Headers[_headerKey];

if (values.Count == 0)
{
ex.Add($"Expected header '{_headerKey}' to be present but no values were found on the response.");
}

}
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
using System.Linq;
using Microsoft.AspNetCore.Http;
namespace Alba

namespace Alba.Assertions
{
internal class NoHeaderValueAssertion : IScenarioAssertion
{
Expand Down
13 changes: 12 additions & 1 deletion src/Alba/HeaderExpectations.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
using System;
using System.Text.RegularExpressions;
using Alba.Assertions;

Expand Down Expand Up @@ -66,12 +67,22 @@ public void ShouldNotBeWritten()
}

/// <summary>
/// Asserts that there are the given values in the Http response
/// Asserts that there are the given values in the header.
/// </summary>
/// <param name="expectedValues"></param>
public void ShouldHaveValues(params string[] expectedValues)
{
if (expectedValues.Length == 0)
throw new ArgumentException("Expected values must contain at least one value", nameof(expectedValues));
_parent.AssertThat(new HeaderMultiValueAssertion(_headerKey, expectedValues));
}

/// <summary>
/// Asserts that there is at least a single header of this name in the Http response.
/// </summary>
public void ShouldHaveValues()
{
_parent.AssertThat(new HeaderExistsAssertion(_headerKey));
}
}
}

0 comments on commit 44b04a2

Please sign in to comment.