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+ }
0 commit comments