Skip to content
This repository was archived by the owner on Sep 3, 2024. It is now read-only.

Commit 82a05da

Browse files
moved acceptable check closer to resources since docs are markdown
consolidated 405 and 406 tests
1 parent 3f3c0ea commit 82a05da

12 files changed

Lines changed: 180 additions & 186 deletions

File tree

src/SqlStreamStore.HAL.Tests/AcceptHeaderTests.cs

Lines changed: 0 additions & 58 deletions
This file was deleted.
Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
namespace SqlStreamStore.HAL.Tests
2+
{
3+
using System;
4+
using System.Collections.Generic;
5+
using System.Collections.Immutable;
6+
using System.Linq;
7+
using System.Net;
8+
using System.Net.Http;
9+
using System.Net.Http.Headers;
10+
using System.Reflection;
11+
using System.Threading.Tasks;
12+
using Shouldly;
13+
using Xunit;
14+
15+
public class ClientErrorTests : IDisposable
16+
{
17+
private static readonly ImmutableHashSet<HttpMethod> s_AllMethods
18+
= ImmutableHashSet.Create(
19+
(from property in typeof(HttpMethod).GetProperties(BindingFlags.Public | BindingFlags.Static)
20+
where property.PropertyType == typeof(HttpMethod)
21+
select (HttpMethod) property.GetValue(null)).ToArray());
22+
23+
private static readonly MediaTypeWithQualityHeaderValue s_HalJson =
24+
MediaTypeWithQualityHeaderValue.Parse(Constants.MediaTypes.HalJson);
25+
26+
private static readonly MediaTypeWithQualityHeaderValue s_TextMarkdown =
27+
MediaTypeWithQualityHeaderValue.Parse(Constants.MediaTypes.TextMarkdown);
28+
29+
private static readonly MediaTypeWithQualityHeaderValue s_TextPlain =
30+
MediaTypeWithQualityHeaderValue.Parse("text/plain");
31+
32+
private static readonly (string requestUri, MediaTypeWithQualityHeaderValue[] notAcceptable, HttpMethod[]
33+
methods)
34+
[] s_ResourceConfigurations =
35+
{
36+
("/", new[] { s_TextMarkdown, s_TextPlain }, new[]
37+
{
38+
HttpMethod.Get,
39+
HttpMethod.Head
40+
}),
41+
("/stream", new[] { s_TextMarkdown, s_TextPlain }, new[]
42+
{
43+
HttpMethod.Get,
44+
HttpMethod.Head
45+
}),
46+
("/streams/a-stream", new[] { s_TextMarkdown, s_TextPlain }, new[]
47+
{
48+
HttpMethod.Get,
49+
HttpMethod.Head,
50+
HttpMethod.Post,
51+
HttpMethod.Delete
52+
}),
53+
("/streams/a-stream/0", new[] { s_TextMarkdown, s_TextPlain }, new[]
54+
{
55+
HttpMethod.Get,
56+
HttpMethod.Head,
57+
HttpMethod.Delete
58+
}),
59+
($"/streams/a-stream/{Guid.Empty}", new[] { s_TextMarkdown, s_TextPlain }, new[]
60+
{
61+
HttpMethod.Get,
62+
HttpMethod.Head,
63+
HttpMethod.Delete
64+
}),
65+
66+
("/streams/a-stream/metadata", new[] { s_TextMarkdown, s_TextPlain }, new[]
67+
{
68+
HttpMethod.Get,
69+
HttpMethod.Head,
70+
HttpMethod.Post
71+
}),
72+
("/docs/doc", new[] { s_HalJson, s_TextPlain }, new[]
73+
{
74+
HttpMethod.Get,
75+
HttpMethod.Head
76+
})
77+
};
78+
79+
80+
private readonly SqlStreamStoreHalMiddlewareFixture _fixture;
81+
82+
public ClientErrorTests()
83+
{
84+
_fixture = new SqlStreamStoreHalMiddlewareFixture();
85+
}
86+
87+
public static IEnumerable<object[]> MethodNotAllowedCases()
88+
=> from _ in s_ResourceConfigurations
89+
let allowed = _.methods.Concat(new[] { HttpMethod.Options }).ToArray() // options are always allowed
90+
from method in s_AllMethods.Except(allowed)
91+
select new object[] { _.requestUri, method, allowed };
92+
93+
[Theory, MemberData(nameof(MethodNotAllowedCases))]
94+
public async Task method_not_allowed(string requestUri, HttpMethod method, HttpMethod[] allowed)
95+
{
96+
using(var response = await _fixture.HttpClient.SendAsync(new HttpRequestMessage(method, requestUri)))
97+
{
98+
response.StatusCode.ShouldBe(HttpStatusCode.MethodNotAllowed);
99+
100+
response.Headers.TryGetValues(Constants.Headers.Allowed, out var allowedHeaders).ShouldBeTrue();
101+
102+
allowedHeaders.ShouldBe(allowed.Select(x => x.Method), true);
103+
}
104+
}
105+
106+
public static IEnumerable<object[]> NotAcceptableCases()
107+
=> from _ in s_ResourceConfigurations
108+
from method in _.methods.Except(new[] { HttpMethod.Delete }).ToArray() // deletes return 204
109+
from notAcceptable in _.notAcceptable
110+
select new object[] { _.requestUri, method, notAcceptable };
111+
112+
[Theory, MemberData(nameof(NotAcceptableCases))]
113+
public async Task not_acceptable(
114+
string requestUri,
115+
HttpMethod method,
116+
MediaTypeWithQualityHeaderValue mediaType)
117+
{
118+
using(var response = await _fixture.HttpClient.SendAsync(new HttpRequestMessage(method, requestUri)
119+
{
120+
Headers = { Accept = { mediaType } }
121+
}))
122+
{
123+
response.StatusCode.ShouldBe(HttpStatusCode.NotAcceptable);
124+
}
125+
}
126+
127+
public void Dispose() => _fixture.Dispose();
128+
}
129+
}

src/SqlStreamStore.HAL.Tests/MethodNotAllowedTests.cs

Lines changed: 0 additions & 98 deletions
This file was deleted.
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
namespace SqlStreamStore.HAL
2+
{
3+
using System.Linq;
4+
using Halcyon.HAL;
5+
using Microsoft.AspNetCore.Builder;
6+
using MidFunc = System.Func<
7+
Microsoft.AspNetCore.Http.HttpContext,
8+
System.Func<System.Threading.Tasks.Task>,
9+
System.Threading.Tasks.Task
10+
>;
11+
12+
internal static class AcceptMiddleware
13+
{
14+
public static IApplicationBuilder UseAccept(this IApplicationBuilder builder, params string[] acceptable)
15+
=> builder.Use(Accept(acceptable));
16+
17+
private static MidFunc Accept(params string[] acceptable) => (context, next) =>
18+
{
19+
var acceptHeaders = context.Request.GetAcceptHeaders();
20+
21+
return acceptHeaders.Any(
22+
acceptHeader => acceptHeader == Constants.MediaTypes.Any || acceptable.Contains(acceptHeader))
23+
? next()
24+
: context.WriteResponse(new HalJsonResponse(new HALResponse(new
25+
{
26+
type = "Not Acceptable",
27+
title = "Not Acceptable",
28+
detail = $"The target resource only understands {string.Join(", ", acceptable)}."
29+
}),
30+
406));
31+
};
32+
}
33+
}

src/SqlStreamStore.HAL/AllStream/AllStreamMiddleware.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,9 @@ private static bool IsAllStream(this PathString pathString)
2727
private static Action<IApplicationBuilder> Configure(AllStreamResource allStream)
2828
=> builder => builder
2929
.UseMiddlewareLogging(typeof(AllStreamMiddleware))
30-
.MapWhen(HttpMethod.Get, inner => inner.Use(GetStream(allStream)))
30+
.MapWhen(
31+
HttpMethod.Get,
32+
inner => inner.UseAccept(Constants.MediaTypes.HalJson).Use(GetStream(allStream)))
3133
.UseAllowedMethods(allStream);
3234

3335
private static MidFunc GetStream(AllStreamResource allStream) => async (context, next) =>

src/SqlStreamStore.HAL/AllStreamMessage/AllStreamMessageMiddleware.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,9 @@ private static bool IsAllStreamMessage(this PathString requestPath)
2727
private static Action<IApplicationBuilder> Configure(AllStreamMessageResource allStreamMessages)
2828
=> builder => builder
2929
.UseMiddlewareLogging(typeof(AllStreamMessageMiddleware))
30-
.MapWhen(HttpMethod.Get, inner => inner.Use(GetStreamMessage(allStreamMessages)))
30+
.MapWhen(
31+
HttpMethod.Get,
32+
inner => inner.UseAccept(Constants.MediaTypes.HalJson).Use(GetStreamMessage(allStreamMessages)))
3133
.UseAllowedMethods(allStreamMessages);
3234

3335
private static MidFunc GetStreamMessage(AllStreamMessageResource allStreamMessages) => async (context, next) =>

src/SqlStreamStore.HAL/Docs/DocsMiddleware.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,8 +38,8 @@ Task Docs(HttpContext context, Func<Task> next)
3838

3939
return builder => builder
4040
.UseMiddlewareLogging(typeof(DocsMiddleware))
41-
.UseAllowedMethods(documentation)
42-
.MapWhen(HttpMethod.Get, inner => inner.Use(Docs));
41+
.MapWhen(HttpMethod.Get, inner => inner.UseAccept(Constants.MediaTypes.TextMarkdown).Use(Docs))
42+
.UseAllowedMethods(documentation);
4343
}
4444
}
4545
}

src/SqlStreamStore.HAL/Index/IndexMiddleware.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ private static bool IsMatch(HttpContext context)
2424
private static Action<IApplicationBuilder> Configure(IndexResource index)
2525
=> builder => builder
2626
.UseMiddlewareLogging(typeof(IndexMiddleware))
27-
.MapWhen(HttpMethod.Get, inner => inner.Use(Index(index)))
27+
.MapWhen(HttpMethod.Get, inner => inner.UseAccept(Constants.MediaTypes.HalJson).Use(Index(index)))
2828
.UseAllowedMethods(index);
2929

3030
private static MidFunc Index(IndexResource index)

0 commit comments

Comments
 (0)