Skip to content

Commit

Permalink
Massive test refactoring
Browse files Browse the repository at this point in the history
- Switch from XUnit to MSTest + FluentAssertions.
- Have tests inherit from RestierTestBase so the TestContext is always available.
- Start standardizing the testing EF models to eventually elminate the TestApiA|TestApiB BS.
- Integration tests should always write the HttpResponse content to the test output for later inspection.
- Implement Breakdance.Restier for all existing integration tests.
- Implement Breakdance Metadata and Visibility Reports for the shared testable contexts.
- Lay the groundwork for more integration tests.
- Man this project needs more unit tests.

[x] All unit tests pass.
  • Loading branch information
robertmclaws committed Jan 21, 2019
1 parent 1d80571 commit 45f858d
Show file tree
Hide file tree
Showing 53 changed files with 1,400 additions and 1,563 deletions.
25 changes: 0 additions & 25 deletions Microsoft.Restier.Tests.Shared/Scenarios/Library/LibraryApi.cs

This file was deleted.

7 changes: 6 additions & 1 deletion RESTier.sln
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,9 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Samples", "Samples", "{DB42
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.Restier.Samples.Northwind.AspNet", "src\Microsoft.Restier.Samples.Northwind.AspNet\Microsoft.Restier.Samples.Northwind.AspNet.csproj", "{3EAB0AED-2BE2-4120-B26E-3401B86C4DC2}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.Restier.Tests.Shared", "Microsoft.Restier.Tests.Shared\Microsoft.Restier.Tests.Shared.csproj", "{B75D79EE-D5C0-4E1B-82CB-9505880A2730}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Restier.Tests.Shared", "src\Microsoft.Restier.Tests.Shared\Microsoft.Restier.Tests.Shared.csproj", "{B75D79EE-D5C0-4E1B-82CB-9505880A2730}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Product", "Product", "{76B4E51F-233E-4DD3-AABF-A6F47788040D}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Expand Down Expand Up @@ -91,6 +93,9 @@ Global
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{D8A3183C-1E9C-4D6C-AC72-4EF938EC9895} = {76B4E51F-233E-4DD3-AABF-A6F47788040D}
{37B52FD3-E72B-406F-8C5A-F146256D7743} = {76B4E51F-233E-4DD3-AABF-A6F47788040D}
{9D3D8728-C31B-4D5E-B471-79A9DBBA0E58} = {76B4E51F-233E-4DD3-AABF-A6F47788040D}
{300B769A-3513-49D0-A035-7DB965C8D2A4} = {D8A3183C-1E9C-4D6C-AC72-4EF938EC9895}
{97E94F97-E73B-4074-8587-AE1B91B4D61E} = {9D3D8728-C31B-4D5E-B471-79A9DBBA0E58}
{8ECF4E97-1816-44AD-AD63-6ACF287ED520} = {9D3D8728-C31B-4D5E-B471-79A9DBBA0E58}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,7 @@ internal static object ConvertValue(
Request = request
};

var returnValue = ODataModelBinderConverter.Convert(
odataValue, propertyType, expectedReturnType, parameterName, readContext, serviceProvider);
var returnValue = ODataModelBinderConverter.Convert(odataValue, propertyType, expectedReturnType, parameterName, readContext, serviceProvider);

if (!propertyType.IsCollection())
{
Expand Down
3 changes: 1 addition & 2 deletions src/Microsoft.Restier.Core/Extensions/ApiBaseExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@
using Microsoft.OData.Edm;
using Microsoft.Restier.Core.Model;
using Microsoft.Restier.Core.Query;
using Microsoft.Restier.Core.Submit;

namespace Microsoft.Restier.Core
{
Expand Down Expand Up @@ -161,7 +160,7 @@ public static IEnumerable<T> GetApiServices<T>(this ApiBase api) where T : class
/// A task that represents the asynchronous
/// operation whose result is the API model.
/// </returns>
public static async Task<IEdmModel> GetModelAsync(this ApiBase api, CancellationToken cancellationToken = default(CancellationToken))
public static async Task<IEdmModel> GetModelAsync(this ApiBase api, CancellationToken cancellationToken = default)
{
Ensure.NotNull(api, nameof(api));

Expand Down
3 changes: 1 addition & 2 deletions src/Microsoft.Restier.Core/Query/DefaultQueryHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -299,8 +299,7 @@ public override Expression Visit(Expression node)
// visited node represents the original starting
// point for the entire composed query, and thus
// it should produce a non-embedded expression.
var constant = node as ConstantExpression;
if (constant == null)
if (!(node is ConstantExpression constant))
{
throw new NotSupportedException(Resources.OriginalExpressionShouldBeConstant);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
<edmx:Edmx Version="4.0" xmlns:edmx="http://docs.oasis-open.org/odata/ns/edmx">
<edmx:DataServices>
<Schema Namespace="Microsoft.Restier.Tests.Shared.Scenarios.Library" xmlns="http://docs.oasis-open.org/odata/ns/edm">
<EntityType Name="Book">
<Key>
<PropertyRef Name="Id" />
</Key>
<Property Name="Id" Type="Edm.Guid" Nullable="false" />
<Property Name="Title" Type="Edm.String" />
<NavigationProperty Name="Publisher" Type="Microsoft.Restier.Tests.Shared.Scenarios.Library.Publisher" />
</EntityType>
<EntityType Name="Publisher">
<Key>
<PropertyRef Name="Id" />
</Key>
<Property Name="Id" Type="Edm.String" Nullable="false" />
<Property Name="Addr" Type="Microsoft.Restier.Tests.Shared.Scenarios.Library.Address" />
<NavigationProperty Name="Books" Type="Collection(Microsoft.Restier.Tests.Shared.Scenarios.Library.Book)" />
</EntityType>
<EntityType Name="Employee">
<Key>
<PropertyRef Name="Id" />
</Key>
<Property Name="Id" Type="Edm.Guid" Nullable="false" />
<Property Name="FullName" Type="Edm.String" />
<Property Name="Addr" Type="Microsoft.Restier.Tests.Shared.Scenarios.Library.Address" />
<Property Name="Universe" Type="Microsoft.Restier.Tests.Shared.Scenarios.Library.Universe" />
</EntityType>
<ComplexType Name="Address">
<Property Name="Street" Type="Edm.String" />
<Property Name="Zip" Type="Edm.String" />
</ComplexType>
<ComplexType Name="Universe">
<Property Name="BinaryProperty" Type="Edm.Binary" />
<Property Name="BooleanProperty" Type="Edm.Boolean" Nullable="false" />
<Property Name="ByteProperty" Type="Edm.Byte" Nullable="false" />
<Property Name="DateProperty" Type="Edm.Date" Nullable="false" />
<Property Name="DateTimeOffsetProperty" Type="Edm.DateTimeOffset" Nullable="false" />
<Property Name="DecimalProperty" Type="Edm.Decimal" Nullable="false" />
<Property Name="DoubleProperty" Type="Edm.Double" Nullable="false" />
<Property Name="DurationProperty" Type="Edm.Duration" Nullable="false" />
<Property Name="GuidProperty" Type="Edm.Guid" Nullable="false" />
<Property Name="Int16Property" Type="Edm.Int16" Nullable="false" />
<Property Name="Int32Property" Type="Edm.Int32" Nullable="false" />
<Property Name="Int64Property" Type="Edm.Int64" Nullable="false" />
<Property Name="SingleProperty" Type="Edm.Single" Nullable="false" />
<Property Name="StringProperty" Type="Edm.String" />
<Property Name="TimeOfDayProperty" Type="Edm.TimeOfDay" Nullable="false" />
</ComplexType>
<Function Name="PublishBook">
<Parameter Name="IsActive" Type="Edm.Boolean" Nullable="false" />
<ReturnType Type="Microsoft.Restier.Tests.Shared.Scenarios.Library.Book" />
</Function>
<EntityContainer Name="Container">
<EntitySet Name="Books" EntityType="Microsoft.Restier.Tests.Shared.Scenarios.Library.Book">
<NavigationPropertyBinding Path="Publisher" Target="Publishers" />
</EntitySet>
<EntitySet Name="Publishers" EntityType="Microsoft.Restier.Tests.Shared.Scenarios.Library.Publisher">
<NavigationPropertyBinding Path="Books" Target="Books" />
</EntitySet>
<EntitySet Name="Readers" EntityType="Microsoft.Restier.Tests.Shared.Scenarios.Library.Employee" />
<FunctionImport Name="PublishBook" Function="Microsoft.Restier.Tests.Shared.Scenarios.Library.PublishBook" EntitySet="Books" />
</EntityContainer>
</Schema>
</edmx:DataServices>
</edmx:Edmx>
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
------------------------------------------------------------
Function Name | Found?
------------------------------------------------------------
CanInsertBook | False
CanUpdateBook | False
CanDeleteBook | False
OnInsertingBook | False
OnUpdatingBook | False
OnDeletingBook | False
OnFilterBooks | False
OnInsertedBook | False
OnUpdatedBook | False
OnDeletedBook | False
CanInsertPublisher | False
CanUpdatePublisher | False
CanDeletePublisher | False
OnInsertingPublisher | False
OnUpdatingPublisher | False
OnDeletingPublisher | False
OnFilterPublishers | False
OnInsertedPublisher | False
OnUpdatedPublisher | False
OnDeletedPublisher | False
CanInsertEmployee | False
CanUpdateEmployee | False
CanDeleteEmployee | False
OnInsertingEmployee | False
OnUpdatingEmployee | False
OnDeletingEmployee | False
OnFilterReaders | False
OnInsertedEmployee | False
OnUpdatedEmployee | False
OnDeletedEmployee | False
CanExecutePublishBook | False
OnExecutingPublishBook | False
OnExecutedPublishBook | False
------------------------------------------------------------
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
<edmx:Edmx Version="4.0" xmlns:edmx="http://docs.oasis-open.org/odata/ns/edmx">
<edmx:DataServices>
<Schema Namespace="Microsoft.Restier.Tests.AspNet" xmlns="http://docs.oasis-open.org/odata/ns/edm">
<EntityType Name="Product">
<Key>
<PropertyRef Name="Id" />
</Key>
<Property Name="Id" Type="Edm.Int32" Nullable="false" />
<Property Name="Name" Type="Edm.String" />
<Property Name="Addr" Type="Microsoft.Restier.Tests.AspNet.Address" Nullable="false" />
<Property Name="Addr2" Type="Microsoft.Restier.Tests.AspNet.Address" />
<Property Name="Addr3" Type="Microsoft.Restier.Tests.AspNet.Address" />
</EntityType>
<EntityType Name="Customer">
<Key>
<PropertyRef Name="Id" />
</Key>
<Property Name="Id" Type="Edm.Int16" Nullable="false" />
</EntityType>
<EntityType Name="Store">
<Key>
<PropertyRef Name="Id" />
</Key>
<Property Name="Id" Type="Edm.Int64" Nullable="false" />
</EntityType>
<ComplexType Name="Address">
<Property Name="Zip" Type="Edm.Int32" Nullable="false" />
</ComplexType>
<Function Name="GetBestProduct">
<ReturnType Type="Microsoft.Restier.Tests.AspNet.Product" />
</Function>
<Action Name="RemoveWorstProduct">
<ReturnType Type="Microsoft.Restier.Tests.AspNet.Product" />
</Action>
<EntityContainer Name="Container">
<EntitySet Name="Products" EntityType="Microsoft.Restier.Tests.AspNet.Product" />
<EntitySet Name="Customers" EntityType="Microsoft.Restier.Tests.AspNet.Customer" />
<EntitySet Name="Stores" EntityType="Microsoft.Restier.Tests.AspNet.Store" />
<FunctionImport Name="GetBestProduct" Function="Microsoft.Restier.Tests.AspNet.GetBestProduct" EntitySet="Products" IncludeInServiceDocument="true" />
<ActionImport Name="RemoveWorstProduct" Action="Microsoft.Restier.Tests.AspNet.RemoveWorstProduct" EntitySet="Products" />
</EntityContainer>
</Schema>
</edmx:DataServices>
</edmx:Edmx>
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
------------------------------------------------------------
Function Name | Found?
------------------------------------------------------------
CanInsertCustomer | False
CanUpdateCustomer | False
CanDeleteCustomer | False
OnInsertingCustomer | False
OnUpdatingCustomer | False
OnDeletingCustomer | False
OnFilterCustomers | False
OnInsertedCustomer | False
OnUpdatedCustomer | False
OnDeletedCustomer | False
CanInsertProduct | False
CanUpdateProduct | False
CanDeleteProduct | False
OnInsertingProduct | False
OnUpdatingProduct | False
OnDeletingProduct | False
OnFilterProducts | False
OnInsertedProduct | False
OnUpdatedProduct | False
OnDeletedProduct | False
CanInsertStore | False
CanUpdateStore | False
CanDeleteStore | False
OnInsertingStore | False
OnUpdatingStore | False
OnDeletingStore | False
OnFilterStores | False
OnInsertedStore | False
OnUpdatedStore | False
OnDeletedStore | False
CanExecuteGetBestProduct | False
OnExecutingGetBestProduct | False
OnExecutedGetBestProduct | False
CanExecuteRemoveWorstProduct | False
OnExecutingRemoveWorstProduct | False
OnExecutedRemoveWorstProduct | False
------------------------------------------------------------
35 changes: 18 additions & 17 deletions src/Microsoft.Restier.Tests.AspNet/ExceptionHandlerTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,42 +4,40 @@
using System.Net.Http;
using System.Security;
using System.Threading.Tasks;
using System.Web.Http;
using CloudNimble.Breakdance.Restier;
using FluentAssertions;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Restier.Core;
using Microsoft.Restier.Core.Query;
using Xunit;
using Microsoft.Restier.Tests.Shared;
using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace Microsoft.Restier.Tests.AspNet
{
public class ExceptionHandlerTests
{
private HttpClient client;

public ExceptionHandlerTests()
{
var configuration = new HttpConfiguration();
configuration.MapRestierRoute<ExcApi>("Exc", "Exc").Wait();
client = new HttpClient(new HttpServer(configuration));
}
[TestClass]
public class ExceptionHandlerTests : RestierTestBase
{

[Fact]
[TestMethod]
public async Task ShouldReturn403HandlerThrowsSecurityException()
{
var request = new HttpRequestMessage(HttpMethod.Get, "http://host/Exc/Products");
var response = await client.SendAsync(request);
Assert.Equal(HttpStatusCode.Forbidden, response.StatusCode);
var response = await RestierTestHelpers.ExecuteTestRequest<SecurityExceptionApi>(HttpMethod.Get, resource: "/Products");
response.IsSuccessStatusCode.Should().BeFalse();
response.StatusCode.Should().Be(HttpStatusCode.Forbidden);
}

private class ExcApi : StoreApi
#region Test Resources

private class SecurityExceptionApi : StoreApi
{
public static new IServiceCollection ConfigureApi(Type apiType, IServiceCollection services)
{
return StoreApi.ConfigureApi(apiType, services)
.AddService<IQueryExpressionSourcer>((sp, next) => new FakeSourcer());
}

public ExcApi(IServiceProvider serviceProvider) : base(serviceProvider)
public SecurityExceptionApi(IServiceProvider serviceProvider) : base(serviceProvider)
{
}
}
Expand All @@ -51,5 +49,8 @@ public Expression ReplaceQueryableSource(QueryExpressionContext context, bool em
throw new SecurityException();
}
}

#endregion

}
}
21 changes: 14 additions & 7 deletions src/Microsoft.Restier.Tests.AspNet/FeatureTests/ExpandTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,23 +2,30 @@
using System.Threading.Tasks;
using CloudNimble.Breakdance.Restier;
using FluentAssertions;
using Microsoft.Restier.Tests.Shared;
using Microsoft.Restier.Tests.Shared.Scenarios.Library;
using Xunit;
using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace Microsoft.Restier.Tests.AspNet.FeatureTests
{
public class ExpandTests

/// <summary>
///
/// </summary>
[TestClass]
public class ExpandTests : RestierTestBase
{

[Fact]
[TestMethod]
public async Task CountPlusExpandShouldntThrowExceptions()
{
var client = await RestierTestHelpers.GetTestableHttpClient<LibraryApi>();
var response = await client.ExecuteTestRequest(HttpMethod.Get, resource: "/Publishers?$expand=Books");
var response = await RestierTestHelpers.ExecuteTestRequest<LibraryApi>(HttpMethod.Get, resource: "/Publishers?$expand=Books");
var content = await response.Content.ReadAsStringAsync();

TestContext.WriteLine(content);
response.IsSuccessStatusCode.Should().BeTrue();
content.Should().Contain("A Clockwork Orange");
}

}
}

}
Loading

0 comments on commit 45f858d

Please sign in to comment.