Skip to content

Add Initial Acceptance Tests #32

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Sep 18, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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