Skip to content

Commit

Permalink
Initial set of acceptance tests (#32)
Browse files Browse the repository at this point in the history
  • Loading branch information
commonsensesoftware authored Sep 18, 2016
1 parent 144ed88 commit f648310
Show file tree
Hide file tree
Showing 92 changed files with 3,262 additions and 0 deletions.
14 changes: 14 additions & 0 deletions ApiVersioning.sln
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,10 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Conventions", "Conventions"
src\Common\Versioning\Conventions\IApiVersionConventionT.cs = src\Common\Versioning\Conventions\IApiVersionConventionT.cs
EndProjectSection
EndProject
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Microsoft.AspNet.WebApi.Acceptance.Tests", "test\Microsoft.AspNet.WebApi.Acceptance.Tests\Microsoft.AspNet.WebApi.Acceptance.Tests.xproj", "{5C31964D-EA8B-420B-9297-5ADFEFE54962}"
EndProject
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Microsoft.AspNetCore.Mvc.Acceptance.Tests", "test\Microsoft.AspNetCore.Mvc.Acceptance.Tests\Microsoft.AspNetCore.Mvc.Acceptance.Tests.xproj", "{4EED304C-D1A6-4866-8D7F-450D084FD25D}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -127,6 +131,14 @@ Global
{D87E54CC-C2D6-4AE5-806D-AE825B051C66}.Debug|Any CPU.Build.0 = Debug|Any CPU
{D87E54CC-C2D6-4AE5-806D-AE825B051C66}.Release|Any CPU.ActiveCfg = Release|Any CPU
{D87E54CC-C2D6-4AE5-806D-AE825B051C66}.Release|Any CPU.Build.0 = Release|Any CPU
{5C31964D-EA8B-420B-9297-5ADFEFE54962}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{5C31964D-EA8B-420B-9297-5ADFEFE54962}.Debug|Any CPU.Build.0 = Debug|Any CPU
{5C31964D-EA8B-420B-9297-5ADFEFE54962}.Release|Any CPU.ActiveCfg = Release|Any CPU
{5C31964D-EA8B-420B-9297-5ADFEFE54962}.Release|Any CPU.Build.0 = Release|Any CPU
{4EED304C-D1A6-4866-8D7F-450D084FD25D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{4EED304C-D1A6-4866-8D7F-450D084FD25D}.Debug|Any CPU.Build.0 = Debug|Any CPU
{4EED304C-D1A6-4866-8D7F-450D084FD25D}.Release|Any CPU.ActiveCfg = Release|Any CPU
{4EED304C-D1A6-4866-8D7F-450D084FD25D}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand All @@ -144,5 +156,7 @@ Global
{AEB074E1-E57A-4DD3-A972-3625B367CE5D} = {0987757E-4D09-4523-B9C9-65B1E8832AA1}
{D87E54CC-C2D6-4AE5-806D-AE825B051C66} = {0987757E-4D09-4523-B9C9-65B1E8832AA1}
{B24995FB-AF48-4E5D-9327-377A599BDE2A} = {DE4EE45F-F8EA-4B32-B16F-441F946ACEF4}
{5C31964D-EA8B-420B-9297-5ADFEFE54962} = {0987757E-4D09-4523-B9C9-65B1E8832AA1}
{4EED304C-D1A6-4866-8D7F-450D084FD25D} = {0987757E-4D09-4523-B9C9-65B1E8832AA1}
EndGlobalSection
EndGlobal
138 changes: 138 additions & 0 deletions test/Microsoft.AspNet.WebApi.Acceptance.Tests/AcceptanceTest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
namespace Microsoft.Web
{
using System;
using System.Collections.Generic;
using System.Net.Http;
using System.Net.Http.Formatting;
using System.Net.Http.Headers;
using System.Threading.Tasks;
using System.Web.Http;
using System.Web.Http.Dispatcher;
using Xunit;
using static System.Net.Http.HttpMethod;
using static System.String;
using static System.Web.Http.IncludeErrorDetailPolicy;

[Trait( "Kind", "Acceptance" )]
public abstract class AcceptanceTest : IDisposable
{
private sealed class FilteredControllerTypeResolver : List<Type>, IHttpControllerTypeResolver
{
public ICollection<Type> GetControllerTypes( IAssembliesResolver assembliesResolver ) => this;
}

private const string JsonMediaType = "application/json";
private static readonly HttpMethod Patch = new HttpMethod( "PATCH" );
private readonly FilteredControllerTypeResolver filteredControllerTypes = new FilteredControllerTypeResolver();
private bool disposed;

~AcceptanceTest()
{
Dispose( false );
}

protected AcceptanceTest()
{
Configuration.IncludeErrorDetailPolicy = Always;
Configuration.Services.Replace( typeof( IHttpControllerTypeResolver ), FilteredControllerTypes );
Server = new HttpServer( Configuration );
Client = new HttpClient( new HttpSimulatorHandler( Server ) )
{
BaseAddress = new Uri( "http://localhost" ),
DefaultRequestHeaders =
{
{ "Host", "localhost" }
}
};
}

protected HttpConfiguration Configuration { get; } = new HttpConfiguration();

protected HttpServer Server { get; }

protected HttpClient Client { get; }

protected IList<Type> FilteredControllerTypes => filteredControllerTypes;

protected virtual void Dispose( bool disposing )
{
if ( disposed )
{
return;
}

disposed = true;

if ( !disposing )
{
return;
}

Client.Dispose();
Server.Dispose();
Configuration.Dispose();
}

public void Dispose()
{
Dispose( true );
GC.SuppressFinalize( this );
}

private HttpRequestMessage CreateRequest<TEntity>( string requestUri, TEntity entity, HttpMethod method )
{
var request = new HttpRequestMessage( method, requestUri );

if ( !Equals( entity, default( TEntity ) ) )
{
var formatter = new JsonMediaTypeFormatter();
request.Content = new ObjectContent<TEntity>( entity, formatter, JsonMediaType );
}

Client.DefaultRequestHeaders.Accept.Add( new MediaTypeWithQualityHeaderValue( JsonMediaType ) );

return request;
}

protected void Accept( string metadata = null )
{
var mediaType = new MediaTypeWithQualityHeaderValue( JsonMediaType );
var odataMetadata = new NameValueHeaderValue( "odata.metadata" );

if ( IsNullOrEmpty( metadata ) )
{
odataMetadata.Value = "none";
}
else
{
switch ( metadata.ToUpperInvariant() )
{
case "NONE":
case "MINIMAL":
case "FULL":
break;
default:
throw new ArgumentOutOfRangeException( nameof( metadata ), "The specified metadata value must be 'none', 'minimal', or 'full'." );
}

odataMetadata.Value = metadata;
}

mediaType.Parameters.Add( odataMetadata );
Client.DefaultRequestHeaders.Accept.Clear();
Client.DefaultRequestHeaders.Accept.Add( mediaType );
}

protected void PreferNoReturn() => Client.DefaultRequestHeaders.Add( "Prefer", "return=representation" );

protected virtual Task<HttpResponseMessage> GetAsync( string requestUri ) => Client.SendAsync( CreateRequest( requestUri, default( object ), Get ) );

protected virtual Task<HttpResponseMessage> PostAsync<TEntity>( string requestUri, TEntity entity ) => Client.SendAsync( CreateRequest( requestUri, entity, Post ) );

protected virtual Task<HttpResponseMessage> PutAsync<TEntity>( string requestUri, TEntity entity ) => Client.SendAsync( CreateRequest( requestUri, entity, Put ) );

protected virtual Task<HttpResponseMessage> PatchAsync<TEntity>( string requestUri, TEntity entity ) => Client.SendAsync( CreateRequest( requestUri, entity, Patch ) );

protected virtual Task<HttpResponseMessage> DeleteAsync( string requestUri ) => Client.SendAsync( CreateRequest( requestUri, default( object ), Delete ) );
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
namespace Microsoft.Web.Http.Basic
{
using Controllers;
using Microsoft.Web.Http.Routing;
using System.Web.Http;
using System.Web.Http.Routing;

public abstract class BasicAcceptanceTest : AcceptanceTest
{
protected BasicAcceptanceTest()
{
var constraintResolver = new DefaultInlineConstraintResolver()
{
ConstraintMap = { ["apiVersion"] = typeof( ApiVersionRouteConstraint ) }
};

FilteredControllerTypes.Add( typeof( ValuesController ) );
FilteredControllerTypes.Add( typeof( Values2Controller ) );
FilteredControllerTypes.Add( typeof( HelloWorldController ) );
Configuration.AddApiVersioning( options => options.ReportApiVersions = true );
Configuration.MapHttpAttributeRoutes( constraintResolver );
Configuration.EnsureInitialized();
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
namespace Microsoft.Web.Http.Basic.Controllers
{
using Microsoft.Web.Http;
using System.Web.Http;

[ApiVersion( "1.0" )]
[RoutePrefix( "api/v{version:apiVersion}/helloworld" )]
public class HelloWorldController : ApiController
{
[Route]
public IHttpActionResult Get() => Ok( new { controller = GetType().Name, version = Request.GetRequestedApiVersion().ToString() } );

[Route( "{id:int}", Name = "GetMessageById" )]
public IHttpActionResult Get( int id ) => Ok( new { controller = GetType().Name, id = id, version = Request.GetRequestedApiVersion().ToString() } );

[Route]
public IHttpActionResult Post() => CreatedAtRoute( "GetMessageById", new { id = 42 }, default( object ) );
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
namespace Microsoft.Web.Http.Basic.Controllers
{
using Microsoft.Web.Http;
using System.Web.Http;

[ApiVersion( "2.0" )]
[Route( "api/values" )]
public class Values2Controller : ApiController
{
public IHttpActionResult Get() => Ok( new { controller = GetType().Name, version = Request.GetRequestedApiVersion().ToString() } );
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
namespace Microsoft.Web.Http.Basic.Controllers
{
using Microsoft.Web.Http;
using System.Web.Http;

[ApiVersion( "1.0" )]
[Route( "api/values" )]
public class ValuesController : ApiController
{
public IHttpActionResult Get() => Ok( new { controller = GetType().Name, version = Request.GetRequestedApiVersion().ToString() } );
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
namespace given
{
using FluentAssertions;
using Microsoft.Web;
using Microsoft.Web.Http.Basic;
using Microsoft.Web.Http.Basic.Controllers;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Threading.Tasks;
using Xunit;
using static System.Net.HttpStatusCode;

public class _a_query_string_versioned_ApiController_split_into_two_types : BasicAcceptanceTest
{
[Theory]
[InlineData( nameof( ValuesController ), "1.0" )]
[InlineData( nameof( Values2Controller ), "2.0" )]
public async Task _get_should_return_200( string controller, string apiVersion )
{
// arrange


// act
var response = await GetAsync( $"api/values?api-version={apiVersion}" ).EnsureSuccessStatusCode();
var content = await response.Content.ReadAsAsync<IDictionary<string, string>>();

// assert
response.Headers.GetValues( "api-supported-versions" ).Single().Should().Be( "1.0, 2.0" );
content.ShouldBeEquivalentTo(
new Dictionary<string, string>()
{
["controller"] = controller,
["version"] = apiVersion
} );
}

[Fact]
public async Task _get_should_return_400_when_version_is_unsupported()
{
// arrange


// act
var response = await GetAsync( "api/values?api-version=3.0" );
var content = await response.Content.ReadAsAsync<OneApiErrorResponse>();

// assert
response.StatusCode.Should().Be( BadRequest );
content.Error.Code.Should().Be( "UnsupportedApiVersion" );
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
namespace given
{
using FluentAssertions;
using Microsoft.Web;
using Microsoft.Web.Http.Basic;
using Microsoft.Web.Http.Basic.Controllers;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Threading.Tasks;
using Xunit;
using static System.Net.HttpStatusCode;

public class _a_url_versioned_ApiController : BasicAcceptanceTest
{
[Theory]
[InlineData( "api/v1/helloworld", null )]
[InlineData( "api/v1/helloworld/42", "42" )]
public async Task _get_should_return_200( string requestUrl, string id )
{
// arrange
var body = new Dictionary<string, string>()
{
["controller"] = nameof( HelloWorldController ),
["version"] = "1"
};

if ( !string.IsNullOrEmpty( id ) )
{
body["id"] = id;
}

// act
var response = await GetAsync( requestUrl ).EnsureSuccessStatusCode();
var content = await response.Content.ReadAsAsync<IDictionary<string, string>>();

// assert
response.Headers.GetValues( "api-supported-versions" ).Single().Should().Be( "1.0" );
content.ShouldBeEquivalentTo( body );
}

[Fact]
public async Task _post_should_return_201()
{
// arrange
var entity = default( object );

// act
var response = await PostAsync( "api/v1/helloworld", entity ).EnsureSuccessStatusCode();

// assert
response.Headers.Location.Should().Be( new Uri( "http://localhost/api/v1/helloworld/42" ) );
}

[Fact]
public async Task _get_should_return_400_when_version_is_unsupported()
{
// arrange


// act
var response = await GetAsync( "api/v2/helloworld" );
var content = await response.Content.ReadAsAsync<OneApiErrorResponse>();

// assert
response.StatusCode.Should().Be( BadRequest );
content.Error.Code.Should().Be( "UnsupportedApiVersion" );
}
}
}
Loading

0 comments on commit f648310

Please sign in to comment.