diff --git a/Microsoft.Restier.Tests.Shared/Scenarios/Library/LibraryApi.cs b/Microsoft.Restier.Tests.Shared/Scenarios/Library/LibraryApi.cs deleted file mode 100644 index 3a7de604..00000000 --- a/Microsoft.Restier.Tests.Shared/Scenarios/Library/LibraryApi.cs +++ /dev/null @@ -1,25 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. See License.txt in the project root for license information. - -using System; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Restier.AspNet; -using Microsoft.Restier.EntityFramework; - -namespace Microsoft.Restier.Tests.Shared.Scenarios.Library -{ - class LibraryApi : EntityFrameworkApi - { - // Need to register publisher services as MapRestierRoute is not called - public static new IServiceCollection ConfigureApi(Type apiType, IServiceCollection services) - { - EntityFrameworkApi.ConfigureApi(apiType, services); - services.AddODataServices(); - return services; - } - - public LibraryApi(IServiceProvider serviceProvider) : base(serviceProvider) - { - } - } -} diff --git a/RESTier.sln b/RESTier.sln index 907bdc11..7480bab3 100644 --- a/RESTier.sln +++ b/RESTier.sln @@ -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 @@ -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} diff --git a/src/Microsoft.Restier.AspNet/Formatter/Deserialization/DeserializationHelpers.cs b/src/Microsoft.Restier.AspNet/Formatter/Deserialization/DeserializationHelpers.cs index 6924ffe0..aa0cf2eb 100644 --- a/src/Microsoft.Restier.AspNet/Formatter/Deserialization/DeserializationHelpers.cs +++ b/src/Microsoft.Restier.AspNet/Formatter/Deserialization/DeserializationHelpers.cs @@ -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()) { diff --git a/src/Microsoft.Restier.Core/Extensions/ApiBaseExtensions.cs b/src/Microsoft.Restier.Core/Extensions/ApiBaseExtensions.cs index 4d37a0e4..a783be15 100644 --- a/src/Microsoft.Restier.Core/Extensions/ApiBaseExtensions.cs +++ b/src/Microsoft.Restier.Core/Extensions/ApiBaseExtensions.cs @@ -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 { @@ -161,7 +160,7 @@ public static IEnumerable GetApiServices(this ApiBase api) where T : class /// A task that represents the asynchronous /// operation whose result is the API model. /// - public static async Task GetModelAsync(this ApiBase api, CancellationToken cancellationToken = default(CancellationToken)) + public static async Task GetModelAsync(this ApiBase api, CancellationToken cancellationToken = default) { Ensure.NotNull(api, nameof(api)); diff --git a/src/Microsoft.Restier.Core/Query/DefaultQueryHandler.cs b/src/Microsoft.Restier.Core/Query/DefaultQueryHandler.cs index c2d95dfc..899db9a6 100644 --- a/src/Microsoft.Restier.Core/Query/DefaultQueryHandler.cs +++ b/src/Microsoft.Restier.Core/Query/DefaultQueryHandler.cs @@ -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); } diff --git a/src/Microsoft.Restier.Tests.AspNet/Baselines/LibraryApi-ApiMetadata.txt b/src/Microsoft.Restier.Tests.AspNet/Baselines/LibraryApi-ApiMetadata.txt new file mode 100644 index 00000000..0ac79d01 --- /dev/null +++ b/src/Microsoft.Restier.Tests.AspNet/Baselines/LibraryApi-ApiMetadata.txt @@ -0,0 +1,66 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/Microsoft.Restier.Tests.AspNet/Baselines/LibraryApi-ApiSurface.txt b/src/Microsoft.Restier.Tests.AspNet/Baselines/LibraryApi-ApiSurface.txt new file mode 100644 index 00000000..2e0610c9 --- /dev/null +++ b/src/Microsoft.Restier.Tests.AspNet/Baselines/LibraryApi-ApiSurface.txt @@ -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 +------------------------------------------------------------ diff --git a/src/Microsoft.Restier.Tests.AspNet/Baselines/StoreApi-ApiMetadata.txt b/src/Microsoft.Restier.Tests.AspNet/Baselines/StoreApi-ApiMetadata.txt new file mode 100644 index 00000000..8d435757 --- /dev/null +++ b/src/Microsoft.Restier.Tests.AspNet/Baselines/StoreApi-ApiMetadata.txt @@ -0,0 +1,44 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/Microsoft.Restier.Tests.AspNet/Baselines/StoreApi-ApiSurface.txt b/src/Microsoft.Restier.Tests.AspNet/Baselines/StoreApi-ApiSurface.txt new file mode 100644 index 00000000..6635bb4c --- /dev/null +++ b/src/Microsoft.Restier.Tests.AspNet/Baselines/StoreApi-ApiSurface.txt @@ -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 +------------------------------------------------------------ diff --git a/src/Microsoft.Restier.Tests.AspNet/ExceptionHandlerTests.cs b/src/Microsoft.Restier.Tests.AspNet/ExceptionHandlerTests.cs index 8fafabe4..529af6d3 100644 --- a/src/Microsoft.Restier.Tests.AspNet/ExceptionHandlerTests.cs +++ b/src/Microsoft.Restier.Tests.AspNet/ExceptionHandlerTests.cs @@ -4,34 +4,32 @@ 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("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(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) { @@ -39,7 +37,7 @@ private class ExcApi : StoreApi .AddService((sp, next) => new FakeSourcer()); } - public ExcApi(IServiceProvider serviceProvider) : base(serviceProvider) + public SecurityExceptionApi(IServiceProvider serviceProvider) : base(serviceProvider) { } } @@ -51,5 +49,8 @@ public Expression ReplaceQueryableSource(QueryExpressionContext context, bool em throw new SecurityException(); } } + + #endregion + } } diff --git a/src/Microsoft.Restier.Tests.AspNet/FeatureTests/ExpandTests.cs b/src/Microsoft.Restier.Tests.AspNet/FeatureTests/ExpandTests.cs index 19ca6819..f52bc133 100644 --- a/src/Microsoft.Restier.Tests.AspNet/FeatureTests/ExpandTests.cs +++ b/src/Microsoft.Restier.Tests.AspNet/FeatureTests/ExpandTests.cs @@ -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 + + /// + /// + /// + [TestClass] + public class ExpandTests : RestierTestBase { - [Fact] + [TestMethod] public async Task CountPlusExpandShouldntThrowExceptions() { - var client = await RestierTestHelpers.GetTestableHttpClient(); - var response = await client.ExecuteTestRequest(HttpMethod.Get, resource: "/Publishers?$expand=Books"); + var response = await RestierTestHelpers.ExecuteTestRequest(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"); } } -} + +} \ No newline at end of file diff --git a/src/Microsoft.Restier.Tests.AspNet/FeatureTests/FunctionTests.cs b/src/Microsoft.Restier.Tests.AspNet/FeatureTests/FunctionTests.cs new file mode 100644 index 00000000..c50d99d8 --- /dev/null +++ b/src/Microsoft.Restier.Tests.AspNet/FeatureTests/FunctionTests.cs @@ -0,0 +1,29 @@ +using System.Net.Http; +using System.Threading.Tasks; +using CloudNimble.Breakdance.Restier; +using FluentAssertions; +using Microsoft.Restier.Tests.Shared; +using Microsoft.Restier.Tests.Shared.Scenarios.Library; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace Microsoft.Restier.Tests.AspNet.FeatureTests +{ + + [TestClass] + public class FunctionTests : RestierTestBase + { + + [Ignore] + [TestMethod] + public async Task FunctionParameters_BooleanParameter () + { + var response = await RestierTestHelpers.ExecuteTestRequest(HttpMethod.Get, resource: "/PublishBook(IsActive=true)"); + var content = await response.Content.ReadAsStringAsync(); + TestContext.WriteLine(content); + response.IsSuccessStatusCode.Should().BeTrue(); + content.Should().Contain("in the Hat"); + } + + } + +} \ No newline at end of file diff --git a/src/Microsoft.Restier.Tests.AspNet/FeatureTests/MetadataTests.cs b/src/Microsoft.Restier.Tests.AspNet/FeatureTests/MetadataTests.cs new file mode 100644 index 00000000..672f57fa --- /dev/null +++ b/src/Microsoft.Restier.Tests.AspNet/FeatureTests/MetadataTests.cs @@ -0,0 +1,115 @@ +using System.IO; +using System.Threading.Tasks; +using CloudNimble.Breakdance.Restier; +using FluentAssertions; +using Microsoft.Restier.Tests.Shared; +using Microsoft.Restier.Tests.Shared.Scenarios.Library; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace Microsoft.Restier.Tests.AspNet.FeatureTests +{ + + [TestClass] + public class MetadataTests : RestierTestBase + { + + #region Private Members + + private const string relativePath = "..//..//..//Baselines//"; + + #endregion + + #region LibraryApi + + [Ignore] + [TestMethod] + public async Task LibraryApi_SaveMetadataDocument() + { + await RestierTestHelpers.WriteCurrentApiMetadata(relativePath); + File.Exists($"{relativePath}{typeof(LibraryApi).Name}-ApiMetadata.txt").Should().BeTrue(); + } + + [Ignore] + [TestMethod] + public async Task LibraryApi_SaveVisibilityMatrix() + { + var api = await RestierTestHelpers.GetTestableApiInstance(); + await api.WriteCurrentVisibilityMatrix(relativePath); + + File.Exists($"{relativePath}{api.GetType().Name}-ApiSurface.txt").Should().BeTrue(); + } + + [TestMethod] + public async Task LibraryApi_CompareCurrentApiMetadataToPriorRun() + { + var fileName = $"{relativePath}{typeof(LibraryApi).Name}-ApiMetadata.txt"; + File.Exists(fileName).Should().BeTrue(); + + var oldReport = File.ReadAllText(fileName); + var newReport = await RestierTestHelpers.GetApiMetadata(); + oldReport.Should().BeEquivalentTo(newReport.ToString()); + } + + [TestMethod] + public async Task LibraryApi_CompareCurrentVisibilityMatrixToPriorRun() + { + var api = await RestierTestHelpers.GetTestableApiInstance(); + var fileName = $"{relativePath}{api.GetType().Name}-ApiSurface.txt"; + + File.Exists(fileName).Should().BeTrue(); + var oldReport = File.ReadAllText(fileName); + var newReport = await api.GenerateVisibilityMatrix(); + oldReport.Should().BeEquivalentTo(newReport); + } + + #endregion + + #region StoreApi + + [Ignore] + [TestMethod] + public async Task StoreApi_SaveMetadataDocument() + { + await RestierTestHelpers.WriteCurrentApiMetadata(relativePath); + File.Exists($"{relativePath}{typeof(StoreApi).Name}-ApiMetadata.txt").Should().BeTrue(); + } + + [Ignore] + [TestMethod] + public async Task StoreApi_SaveVisibilityMatrix() + { + var api = await RestierTestHelpers.GetTestableApiInstance(); + await api.WriteCurrentVisibilityMatrix(relativePath); + + File.Exists($"{relativePath}{api.GetType().Name}-ApiSurface.txt").Should().BeTrue(); + } + + [TestMethod] + public async Task StoreApi_CompareCurrentApiMetadataToPriorRun() + { + var fileName = $"{relativePath}{typeof(StoreApi).Name}-ApiMetadata.txt"; + File.Exists(fileName).Should().BeTrue(); + + var oldReport = File.ReadAllText(fileName); + var newReport = await RestierTestHelpers.GetApiMetadata(); + oldReport.Should().BeEquivalentTo(newReport.ToString()); + } + + [TestMethod] + public async Task StoreApi_CompareCurrentVisibilityMatrixToPriorRun() + { + var api = await RestierTestHelpers.GetTestableApiInstance(); + var fileName = $"{relativePath}{api.GetType().Name}-ApiSurface.txt"; + + File.Exists(fileName).Should().BeTrue(); + var oldReport = File.ReadAllText(fileName); + var newReport = await api.GenerateVisibilityMatrix(); + oldReport.Should().BeEquivalentTo(newReport); + } + + #endregion + + + } + +} \ No newline at end of file diff --git a/src/Microsoft.Restier.Tests.AspNet/Microsoft.Restier.Tests.AspNet.csproj b/src/Microsoft.Restier.Tests.AspNet/Microsoft.Restier.Tests.AspNet.csproj index 4556e202..8f6f2752 100644 --- a/src/Microsoft.Restier.Tests.AspNet/Microsoft.Restier.Tests.AspNet.csproj +++ b/src/Microsoft.Restier.Tests.AspNet/Microsoft.Restier.Tests.AspNet.csproj @@ -5,19 +5,16 @@ - + - - - all - runtime; build; native; contentfiles; analyzers - + + - + diff --git a/src/Microsoft.Restier.Tests.AspNet/Model/RestierModelBuilderTests.cs b/src/Microsoft.Restier.Tests.AspNet/Model/RestierModelBuilderTests.cs index 6debe917..0ab14ac4 100644 --- a/src/Microsoft.Restier.Tests.AspNet/Model/RestierModelBuilderTests.cs +++ b/src/Microsoft.Restier.Tests.AspNet/Model/RestierModelBuilderTests.cs @@ -1,67 +1,66 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. See License.txt in the project root for license information. -using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using CloudNimble.Breakdance.Restier; -using Microsoft.Extensions.DependencyInjection; +using FluentAssertions; using Microsoft.OData.Edm; using Microsoft.OData.Edm.Validation; -using Microsoft.Restier.Core; +using Microsoft.Restier.Tests.Shared; using Microsoft.Restier.Tests.Shared.Scenarios.Library; -using Xunit; +using Microsoft.VisualStudio.TestTools.UnitTesting; namespace Microsoft.Restier.Tests.AspNet.Model { - public class RestierModelBuilderTests + + [TestClass] + public class RestierModelBuilderTests : RestierTestBase { - [Fact] + [TestMethod] public async Task ComplexTypeShoudWork() { - var api = await RestierTestHelpers.GetTestableApiInstance(); - var model = await api.GetModelAsync(); + var model = await RestierTestHelpers.GetTestableModelAsync(); - Assert.True(model.Validate(out var errors)); - Assert.Empty(errors); + model.Validate(out var errors).Should().BeTrue(); + errors.Should().BeEmpty(); var address = model.FindDeclaredType("Microsoft.Restier.Tests.Shared.Scenarios.Library.Address") as IEdmComplexType; - Assert.NotNull(address); - Assert.Equal(2, address.Properties().Count()); + address.Should().NotBeNull(); + address.Properties().Should().HaveCount(2); } - [Fact] + [TestMethod] public async Task PrimitiveTypesShouldWork() { - var api = await RestierTestHelpers.GetTestableApiInstance(); - var model = await api.GetModelAsync(); + var model = await RestierTestHelpers.GetTestableModelAsync(); - Assert.True(model.Validate(out var errors)); - Assert.Empty(errors); + model.Validate(out var errors).Should().BeTrue(); + errors.Should().BeEmpty(); var universe = model.FindDeclaredType("Microsoft.Restier.Tests.Shared.Scenarios.Library.Universe") as IEdmComplexType; - Assert.NotNull(universe); + universe.Should().NotBeNull(); var propertyArray = universe.Properties().ToArray(); var i = 0; - Assert.True(propertyArray[i++].Type.AsPrimitive().IsBinary()); - Assert.True(propertyArray[i++].Type.AsPrimitive().IsBoolean()); - Assert.True(propertyArray[i++].Type.AsPrimitive().IsByte()); - Assert.True(propertyArray[i++].Type.AsPrimitive().IsDate()); - Assert.True(propertyArray[i++].Type.AsPrimitive().IsDateTimeOffset()); - Assert.True(propertyArray[i++].Type.AsPrimitive().IsDecimal()); - Assert.True(propertyArray[i++].Type.AsPrimitive().IsDouble()); - Assert.True(propertyArray[i++].Type.AsPrimitive().IsDuration()); - Assert.True(propertyArray[i++].Type.AsPrimitive().IsGuid()); - Assert.True(propertyArray[i++].Type.AsPrimitive().IsInt16()); - Assert.True(propertyArray[i++].Type.AsPrimitive().IsInt32()); - Assert.True(propertyArray[i++].Type.AsPrimitive().IsInt64()); - // Assert.True(propertyArray[i++].Type.AsPrimitive().IsSByte()); - Assert.True(propertyArray[i++].Type.AsPrimitive().IsSingle()); - // Assert.True(propertyArray[i++].Type.AsPrimitive().IsStream()); - Assert.True(propertyArray[i++].Type.AsPrimitive().IsString()); - // Assert.True(propertyArray[i].Type.AsPrimitive().IsTimeOfDay()); + propertyArray[i++].Type.AsPrimitive().IsBinary().Should().BeTrue(); + propertyArray[i++].Type.AsPrimitive().IsBoolean().Should().BeTrue(); + propertyArray[i++].Type.AsPrimitive().IsByte().Should().BeTrue(); + propertyArray[i++].Type.AsPrimitive().IsDate().Should().BeTrue(); + propertyArray[i++].Type.AsPrimitive().IsDateTimeOffset().Should().BeTrue(); + propertyArray[i++].Type.AsPrimitive().IsDecimal().Should().BeTrue(); + propertyArray[i++].Type.AsPrimitive().IsDouble().Should().BeTrue(); + propertyArray[i++].Type.AsPrimitive().IsDuration().Should().BeTrue(); + propertyArray[i++].Type.AsPrimitive().IsGuid().Should().BeTrue(); + propertyArray[i++].Type.AsPrimitive().IsInt16().Should().BeTrue(); + propertyArray[i++].Type.AsPrimitive().IsInt32().Should().BeTrue(); + propertyArray[i++].Type.AsPrimitive().IsInt64().Should().BeTrue(); + // propertyArray[i++].Type.AsPrimitive().IsSByte().Should().BeTrue(); + propertyArray[i++].Type.AsPrimitive().IsSingle().Should().BeTrue(); + // propertyArray[i++].Type.AsPrimitive().IsStream().Should().BeTrue(); + propertyArray[i++].Type.AsPrimitive().IsString().Should().BeTrue(); + // propertyArray[i].Type.AsPrimitive().IsTimeOfDay().Should().BeTrue(); } } } diff --git a/src/Microsoft.Restier.Tests.AspNet/Model/RestierModelExtender.Tests.cs b/src/Microsoft.Restier.Tests.AspNet/Model/RestierModelExtenderTests.cs similarity index 66% rename from src/Microsoft.Restier.Tests.AspNet/Model/RestierModelExtender.Tests.cs rename to src/Microsoft.Restier.Tests.AspNet/Model/RestierModelExtenderTests.cs index 204bc55c..2d30a617 100644 --- a/src/Microsoft.Restier.Tests.AspNet/Model/RestierModelExtender.Tests.cs +++ b/src/Microsoft.Restier.Tests.AspNet/Model/RestierModelExtenderTests.cs @@ -1,155 +1,150 @@ -using Microsoft.AspNet.OData.Extensions; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using CloudNimble.Breakdance.Restier; +using FluentAssertions; +using Microsoft.AspNet.OData.Extensions; using Microsoft.Extensions.DependencyInjection; using Microsoft.OData.Edm; using Microsoft.Restier.AspNet.Model; using Microsoft.Restier.Core; using Microsoft.Restier.Core.Model; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Net.Http; -using System.Threading; -using System.Threading.Tasks; -using System.Web.Http; -using Xunit; +using Microsoft.Restier.Tests.Shared; +using Microsoft.VisualStudio.TestTools.UnitTesting; namespace Microsoft.Restier.Tests.AspNet.Model { - public class RestierModelExtenderTests + + [TestClass] + public class RestierModelExtenderTests : RestierTestBase { - [Fact] + + [TestMethod] public async Task ApiModelBuilder_ShouldProduceEmptyModelForEmptyApi() { - var model = await GetModelAsync(); - Assert.Single(model.SchemaElements); - Assert.Empty(model.EntityContainer.Elements); + var model = await RestierTestHelpers.GetTestableModelAsync(); + model.SchemaElements.Should().HaveCount(1); + model.EntityContainer.Elements.Should().BeEmpty(); } - [Fact] + [TestMethod] public async Task ApiModelBuilder_ShouldProduceCorrectModelForBasicScenario() { - var model = await GetModelAsync(); - Assert.DoesNotContain("ApiConfiguration", model.EntityContainer.Elements.Select(e => e.Name)); - Assert.DoesNotContain("Invisible", model.EntityContainer.Elements.Select(e => e.Name)); - Assert.NotNull(model.EntityContainer.FindEntitySet("People")); - Assert.NotNull(model.EntityContainer.FindSingleton("Me")); + var model = await RestierTestHelpers.GetTestableModelAsync(); + model.EntityContainer.Elements.Select(e => e.Name).Should().NotContain("ApiConfiguration"); + model.EntityContainer.Elements.Select(e => e.Name).Should().NotContain("Invisible"); + model.EntityContainer.FindEntitySet("People").Should().NotBeNull(); + model.EntityContainer.FindSingleton("Me").Should().NotBeNull(); } - [Fact] + [TestMethod] public async Task ApiModelBuilder_ShouldProduceCorrectModelForDerivedApi() { - var model = await GetModelAsync(); - Assert.DoesNotContain("ApiConfiguration", model.EntityContainer.Elements.Select(e => e.Name)); - Assert.DoesNotContain("Invisible", model.EntityContainer.Elements.Select(e => e.Name)); - Assert.NotNull(model.EntityContainer.FindEntitySet("People")); - Assert.NotNull(model.EntityContainer.FindEntitySet("Customers")); - Assert.NotNull(model.EntityContainer.FindSingleton("Me")); + var model = await RestierTestHelpers.GetTestableModelAsync(); + model.EntityContainer.Elements.Select(e => e.Name).Should().NotContain("ApiConfiguration"); + model.EntityContainer.Elements.Select(e => e.Name).Should().NotContain("Invisible"); + model.EntityContainer.FindEntitySet("Customers").Should().NotBeNull(); + model.EntityContainer.FindSingleton("Me").Should().NotBeNull(); + model.EntityContainer.FindEntitySet("People").Should().NotBeNull(); } - [Fact] + [TestMethod] public async Task ApiModelBuilder_ShouldProduceCorrectModelForOverridingProperty() { - var model = await GetModelAsync(); - Assert.DoesNotContain("ApiConfiguration", model.EntityContainer.Elements.Select(e => e.Name)); - Assert.DoesNotContain("Invisible", model.EntityContainer.Elements.Select(e => e.Name)); - Assert.NotNull(model.EntityContainer.FindEntitySet("People")); - Assert.Equal("Customer", model.EntityContainer.FindEntitySet("Customers").EntityType().Name); - Assert.Equal("Customer", model.EntityContainer.FindSingleton("Me").EntityType().Name); + var model = await RestierTestHelpers.GetTestableModelAsync(); + model.EntityContainer.Elements.Select(e => e.Name).Should().NotContain("ApiConfiguration"); + model.EntityContainer.Elements.Select(e => e.Name).Should().NotContain("Invisible"); + model.EntityContainer.FindEntitySet("People").Should().NotBeNull(); + model.EntityContainer.FindEntitySet("Customers").EntityType().Name.Should().Be("Customer"); + model.EntityContainer.FindSingleton("Me").EntityType().Name.Should().Be("Customer"); } - [Fact] + [TestMethod] public async Task ApiModelBuilder_ShouldProduceCorrectModelForIgnoringInheritedProperty() { - var model = await GetModelAsync(); - Assert.DoesNotContain("ApiConfiguration", model.EntityContainer.Elements.Select(e => e.Name)); - Assert.DoesNotContain("Invisible", model.EntityContainer.Elements.Select(e => e.Name)); - Assert.Equal("Customer", model.EntityContainer.FindEntitySet("Customers").EntityType().Name); - Assert.Equal("Customer", model.EntityContainer.FindSingleton("Me").EntityType().Name); + var model = await RestierTestHelpers.GetTestableModelAsync(); + model.EntityContainer.Elements.Select(e => e.Name).Should().NotContain("ApiConfiguration"); + model.EntityContainer.Elements.Select(e => e.Name).Should().NotContain("Invisible"); + model.EntityContainer.FindEntitySet("Customers").EntityType().Name.Should().Be("Customer"); + model.EntityContainer.FindSingleton("Me").EntityType().Name.Should().Be("Customer"); } - [Fact] + [TestMethod] public async Task ApiModelBuilder_ShouldSkipEntitySetWithUndeclaredType() { - var model = await GetModelAsync(); - Assert.Equal("Person", model.EntityContainer.FindEntitySet("People").EntityType().Name); - Assert.DoesNotContain("Orders", model.EntityContainer.Elements.Select(e => e.Name)); + var model = await RestierTestHelpers.GetTestableModelAsync(); + model.EntityContainer.FindEntitySet("People").EntityType().Name.Should().Be("Person"); + model.EntityContainer.Elements.Select(e => e.Name).Should().NotContain("Orders"); } - [Fact] + [TestMethod] public async Task ApiModelBuilder_ShouldSkipExistingEntitySet() { - var model = await GetModelAsync(); - Assert.Equal("VipCustomer", model.EntityContainer.FindEntitySet("VipCustomers").EntityType().Name); + var model = await RestierTestHelpers.GetTestableModelAsync(); + model.EntityContainer.FindEntitySet("VipCustomers").EntityType().Name.Should().Be("VipCustomer"); } - [Fact] + [TestMethod] public async Task ApiModelBuilder_ShouldCorrectlyAddBindingsForCollectionNavigationProperty() { // In this case, only one entity set People has entity type Person. // Bindings for collection navigation property Customer.Friends should be added. // Bindings for singleton navigation property Customer.BestFriend should be added. - var model = await GetModelAsync(); + var model = await RestierTestHelpers.GetTestableModelAsync(); var customersBindings = model.EntityContainer.FindEntitySet("Customers").NavigationPropertyBindings.ToArray(); var friendsBinding = customersBindings.FirstOrDefault(c => c.NavigationProperty.Name == "Friends"); - Assert.NotNull(friendsBinding); - Assert.Equal("People", friendsBinding.Target.Name); + friendsBinding.Should().NotBeNull(); + friendsBinding.Target.Name.Should().Be("People"); var bestFriendBinding = customersBindings.FirstOrDefault(c => c.NavigationProperty.Name == "BestFriend"); - Assert.NotNull(bestFriendBinding); - Assert.Equal("People", bestFriendBinding.Target.Name); + bestFriendBinding.Should().NotBeNull(); + bestFriendBinding.Target.Name.Should().Be("People"); var meBindings = model.EntityContainer.FindSingleton("Me").NavigationPropertyBindings.ToArray(); var friendsBinding2 = meBindings.FirstOrDefault(c => c.NavigationProperty.Name == "Friends"); - Assert.NotNull(friendsBinding2); - Assert.Equal("People", friendsBinding2.Target.Name); + friendsBinding2.Should().NotBeNull(); + friendsBinding2.Target.Name.Should().Be("People"); var bestFriendBinding2 = meBindings.FirstOrDefault(c => c.NavigationProperty.Name == "BestFriend"); - Assert.NotNull(bestFriendBinding2); - Assert.Equal("People", bestFriendBinding2.Target.Name); + bestFriendBinding2.Should().NotBeNull(); + bestFriendBinding2.Target.Name.Should().Be("People"); } - [Fact] + [TestMethod] public async Task ApiModelBuilder_ShouldCorrectlyAddBindingsForSingletonNavigationProperty() { // In this case, only one singleton Me has entity type Person. // Bindings for collection navigation property Customer.Friends should NOT be added. // Bindings for singleton navigation property Customer.BestFriend should be added. - var model = await GetModelAsync(); + var model = await RestierTestHelpers.GetTestableModelAsync(); var binding = model.EntityContainer.FindEntitySet("Customers").NavigationPropertyBindings.Single(); - Assert.Equal("BestFriend", binding.NavigationProperty.Name); - Assert.Equal("Me", binding.Target.Name); + binding.NavigationProperty.Name.Should().Be("BestFriend"); + binding.Target.Name.Should().Be("Me"); binding = model.EntityContainer.FindSingleton("Me2").NavigationPropertyBindings.Single(); - Assert.Equal("BestFriend", binding.NavigationProperty.Name); - Assert.Equal("Me", binding.Target.Name); + binding.NavigationProperty.Name.Should().Be("BestFriend"); + binding.Target.Name.Should().Be("Me"); } - [Fact] + [TestMethod] public async Task ApiModelBuilder_ShouldNotAddAmbiguousNavigationPropertyBindings() { // In this case, two entity sets Employees and People have entity type Person. // Bindings for collection navigation property Customer.Friends should NOT be added. // Bindings for singleton navigation property Customer.BestFriend should NOT be added. - var model = await GetModelAsync(); - Assert.Empty(model.EntityContainer.FindEntitySet("Customers").NavigationPropertyBindings); - Assert.Empty(model.EntityContainer.FindSingleton("Me").NavigationPropertyBindings); + var model = await RestierTestHelpers.GetTestableModelAsync(); + model.EntityContainer.FindEntitySet("Customers").NavigationPropertyBindings.Should().BeEmpty(); + model.EntityContainer.FindSingleton("Me").NavigationPropertyBindings.Should().BeEmpty(); } - private async Task GetModelAsync() where T : BaseApi - { - var config = new HttpConfiguration(); - await config.MapRestierRoute( - "test", "api/test", null); - - var request = new HttpRequestMessage(HttpMethod.Get, "http://localhost/api/test"); - request.SetConfiguration(config); - var api = request.CreateRequestContainer("test").GetService(); - return await api.GetModelAsync(); - } } + #region Test Resources + public class TestModelBuilder : IModelBuilder { public Task GetModelAsync(ModelContext context, CancellationToken cancellationToken) @@ -328,4 +323,7 @@ public ApiH(IServiceProvider serviceProvider) : base(serviceProvider) { } } + + #endregion + } \ No newline at end of file diff --git a/src/Microsoft.Restier.Tests.AspNet/FallbackTests.cs b/src/Microsoft.Restier.Tests.AspNet/ODataControllerFallbackTests.cs similarity index 58% rename from src/Microsoft.Restier.Tests.AspNet/FallbackTests.cs rename to src/Microsoft.Restier.Tests.AspNet/ODataControllerFallbackTests.cs index 78fff204..ec2cfe93 100644 --- a/src/Microsoft.Restier.Tests.AspNet/FallbackTests.cs +++ b/src/Microsoft.Restier.Tests.AspNet/ODataControllerFallbackTests.cs @@ -5,11 +5,11 @@ using System.Collections.Generic; using System.Linq; using System.Linq.Expressions; -using System.Net; using System.Net.Http; -using System.Net.Http.Headers; using System.Threading.Tasks; using System.Web.Http; +using CloudNimble.Breakdance.Restier; +using FluentAssertions; using Microsoft.AspNet.OData; using Microsoft.AspNet.OData.Builder; using Microsoft.Extensions.DependencyInjection; @@ -18,69 +18,61 @@ using Microsoft.Restier.Core; using Microsoft.Restier.Core.Model; using Microsoft.Restier.Core.Query; -using Xunit; +using Microsoft.Restier.Tests.Shared; +using Microsoft.VisualStudio.TestTools.UnitTesting; namespace Microsoft.Restier.Tests.AspNet { - public class FallbackTests - { - private HttpClient client; - public FallbackTests() - { - var configuration = new HttpConfiguration(); - configuration.MapRestierRoute("fallback", "fallback").Wait(); - client = new HttpClient(new HttpServer(configuration)); - } + [TestClass] + public class ODataControllerFallbackTests : RestierTestBase + { - [Fact] - public async Task FallbackEntitySetTest() + [TestMethod] + public async Task FallbackApi_EntitySet_ShouldFallBack() { // Should fallback to PeopleController. - var request = new HttpRequestMessage(HttpMethod.Get, "http://host/fallback/People"); - request.Headers.Accept.Add(MediaTypeWithQualityHeaderValue.Parse("application/json;odata.metadata=full")); - var response = await client.SendAsync(request); - Assert.Equal(HttpStatusCode.OK, response.StatusCode); - Assert.Equal(999, ((Person[])((ObjectContent)response.Content).Value).Single().Id); + var response = await RestierTestHelpers.ExecuteTestRequest(HttpMethod.Get, resource: "/People"); + TestContext.WriteLine(await response.Content.ReadAsStringAsync()); + response.IsSuccessStatusCode.Should().BeTrue(); + ((Person[])((ObjectContent)response.Content).Value).Single().Id.Should().Be(999); } - [Fact] - public async Task FallbackNavigationPropertyTest() + [TestMethod] + public async Task FallbackApi_NavigationProperty_ShouldFallBack() { // Should fallback to PeopleController. - var request = new HttpRequestMessage(HttpMethod.Get, "http://host/fallback/People(1)/Orders"); - request.Headers.Accept.Add(MediaTypeWithQualityHeaderValue.Parse("application/json;odata.metadata=full")); - var response = await client.SendAsync(request); - Assert.Equal(HttpStatusCode.OK, response.StatusCode); - Assert.Equal(123, ((Order[])((ObjectContent)response.Content).Value).Single().Id); + var response = await RestierTestHelpers.ExecuteTestRequest(HttpMethod.Get, resource: "/People(1)/Orders"); + TestContext.WriteLine(await response.Content.ReadAsStringAsync()); + response.IsSuccessStatusCode.Should().BeTrue(); + ((Order[])((ObjectContent)response.Content).Value).Single().Id.Should().Be(123); } - [Fact] - public async Task NonFallbackTest() + [TestMethod] + public async Task FallbackApi_EntitySet_ShouldNotFallBack() { // Should be routed to RestierController. - var request = new HttpRequestMessage(HttpMethod.Get, "http://host/fallback/Orders"); - request.Headers.Accept.Add(MediaTypeWithQualityHeaderValue.Parse("application/json;odata.metadata=full")); - var response = await client.SendAsync(request); - Assert.Equal(HttpStatusCode.OK, response.StatusCode); - var payload = await ((ObjectContent)response.Content).ReadAsStringAsync(); - Assert.Contains("\"Id\":234", payload); + var response = await RestierTestHelpers.ExecuteTestRequest(HttpMethod.Get, resource: "/Orders"); + TestContext.WriteLine(await response.Content.ReadAsStringAsync()); + response.IsSuccessStatusCode.Should().BeTrue(); + (await response.Content.ReadAsStringAsync()).Should().Contain("\"Id\":234"); } - [Fact] - public async Task FallbackConventionBasedProviderTest() + [TestMethod] + public async Task FallbackApi_Resource_ShouldNotFallBack() { // Should be routed to RestierController. - var request = new HttpRequestMessage(HttpMethod.Get, "http://host/fallback/PreservedOrders"); - request.Headers.Accept.Add(MediaTypeWithQualityHeaderValue.Parse("application/json;odata.metadata=full")); - var response = await client.SendAsync(request); - Assert.Equal(HttpStatusCode.OK, response.StatusCode); - var payload = await ((ObjectContent)response.Content).ReadAsStringAsync(); - Assert.Contains("\"Id\":234", payload); + var response = await RestierTestHelpers.ExecuteTestRequest(HttpMethod.Get, resource: "/PreservedOrders"); + TestContext.WriteLine(await response.Content.ReadAsStringAsync()); + response.IsSuccessStatusCode.Should().BeTrue(); + (await response.Content.ReadAsStringAsync()).Should().Contain("\"Id\":234"); } + } - public static class FallbackModel + #region Test Resources + + internal static class FallbackModel { public static EdmModel Model { get; private set; } @@ -113,6 +105,7 @@ internal class FallbackApi : ApiBase public FallbackApi(IServiceProvider serviceProvider) : base(serviceProvider) { } + } public class PeopleController : ODataController @@ -138,19 +131,19 @@ public IHttpActionResult GetOrders(int key) } } - class Person + internal class Person { public int Id { get; set; } public IEnumerable Orders { get; set; } } - class Order + internal class Order { public int Id { get; set; } } - class FallbackQueryExpressionSourcer : IQueryExpressionSourcer + internal class FallbackQueryExpressionSourcer : IQueryExpressionSourcer { public Expression ReplaceQueryableSource(QueryExpressionContext context, bool embedded) { @@ -171,7 +164,7 @@ public Expression ReplaceQueryableSource(QueryExpressionContext context, bool em } } - class FallbackModelMapper : IModelMapper + internal class FallbackModelMapper : IModelMapper { public bool TryGetRelevantType(ModelContext context, string name, out Type relevantType) { @@ -182,4 +175,8 @@ public bool TryGetRelevantType(ModelContext context, string name, out Type relev public bool TryGetRelevantType(ModelContext context, string namespaceName, string name, out Type relevantType) => TryGetRelevantType(context, name, out relevantType); } -} + + #endregion + + +} \ No newline at end of file diff --git a/src/Microsoft.Restier.Tests.AspNet/Properties/AssemblyInfo.cs b/src/Microsoft.Restier.Tests.AspNet/Properties/AssemblyInfo.cs deleted file mode 100644 index 7fcef158..00000000 --- a/src/Microsoft.Restier.Tests.AspNet/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,39 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. See License.txt in the project root for license information. - -using System.Reflection; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; - -// General Information about an assembly is controlled through the following -// set of attributes. Change these attribute values to modify the information -// associated with an assembly. -//[assembly: AssemblyTitle("Microsoft.Restier.Tests.AspNet")] -//[assembly: AssemblyDescription("")] -//[assembly: AssemblyConfiguration("")] -//[assembly: AssemblyCompany("")] -//[assembly: AssemblyProduct("Microsoft.Restier.Tests.AspNet")] -//[assembly: AssemblyCopyright("Copyright © 2014")] -//[assembly: AssemblyTrademark("")] -//[assembly: AssemblyCulture("")] - -// Setting ComVisible to false makes the types in this assembly not visible -// to COM components. If you need to access a type in this assembly from -// COM, set the ComVisible attribute to true on that type. -[assembly: ComVisible(false)] - -// The following GUID is for the ID of the typelib if this project is exposed to COM -[assembly: Guid("cb6c5ef3-979e-4748-acab-70b3bfa14d4d")] - -// Version information for an assembly consists of the following four values: -// -// Major Version -// Minor Version -// Build Number -// Revision -// -// You can specify all the values or you can default the Build and Revision Numbers -// by using the '*' as shown below: -// [assembly: AssemblyVersion("1.0.*")] -//[assembly: AssemblyVersion("1.0.0.0")] -//[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/src/Microsoft.Restier.Tests.AspNet/RegressionTests/Issue541_CountPlusParametersFails.cs b/src/Microsoft.Restier.Tests.AspNet/RegressionTests/Issue541_CountPlusParametersFails.cs index 360a8c06..7df2658c 100644 --- a/src/Microsoft.Restier.Tests.AspNet/RegressionTests/Issue541_CountPlusParametersFails.cs +++ b/src/Microsoft.Restier.Tests.AspNet/RegressionTests/Issue541_CountPlusParametersFails.cs @@ -2,8 +2,9 @@ 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.RegressionTests { @@ -11,10 +12,10 @@ namespace Microsoft.Restier.Tests.AspNet.RegressionTests /// /// Regression tests for https://github.com/OData/RESTier/issues/541. /// - public class Issue541_CountPlusParametersFails + public class Issue541_CountPlusParametersFails : RestierTestBase { - [Fact] + [TestMethod] public async Task CountShouldntThrowExceptions() { var client = await RestierTestHelpers.GetTestableHttpClient(); @@ -22,7 +23,7 @@ public async Task CountShouldntThrowExceptions() response.Should().Contain("\"@odata.count\":2,"); } - [Fact] + [TestMethod] public async Task CountPlusTopShouldntThrowExceptions() { var client = await RestierTestHelpers.GetTestableHttpClient(); @@ -30,7 +31,7 @@ public async Task CountPlusTopShouldntThrowExceptions() response.Should().Contain("\"@odata.count\":2,"); } - [Fact] + [TestMethod] public async Task CountPlusTopPlusFilterShouldntThrowExceptions() { var client = await RestierTestHelpers.GetTestableHttpClient(); @@ -38,7 +39,7 @@ public async Task CountPlusTopPlusFilterShouldntThrowExceptions() response.Should().Contain("\"@odata.count\":1,"); } - [Fact] + [TestMethod] public async Task CountPlusTopPlusProjectionShouldntThrowExceptions() { var client = await RestierTestHelpers.GetTestableHttpClient(); @@ -48,7 +49,7 @@ public async Task CountPlusTopPlusProjectionShouldntThrowExceptions() content.Should().Contain("\"@odata.count\":2,"); } - [Fact] + [TestMethod] public async Task CountPlusSelectShouldntThrowExceptions() { var client = await RestierTestHelpers.GetTestableHttpClient(); @@ -58,7 +59,7 @@ public async Task CountPlusSelectShouldntThrowExceptions() content.Should().Contain("\"@odata.count\":2,"); } - [Fact] + [TestMethod] public async Task CountPlusExpandShouldntThrowExceptions() { var client = await RestierTestHelpers.GetTestableHttpClient(); diff --git a/src/Microsoft.Restier.Tests.AspNet/RestierControllerTests.cs b/src/Microsoft.Restier.Tests.AspNet/RestierControllerTests.cs index 2ed89b25..078ba1f6 100644 --- a/src/Microsoft.Restier.Tests.AspNet/RestierControllerTests.cs +++ b/src/Microsoft.Restier.Tests.AspNet/RestierControllerTests.cs @@ -3,167 +3,109 @@ using System.Net; using System.Net.Http; -using System.Net.Http.Headers; -using System.Text; using System.Threading.Tasks; -using System.Web.Http; -using Xunit; +using CloudNimble.Breakdance.Restier; +using CloudNimble.Breakdance.WebApi; +using FluentAssertions; +using Microsoft.Restier.Tests.Shared; +using Microsoft.VisualStudio.TestTools.UnitTesting; namespace Microsoft.Restier.Tests.AspNet { - public class RestierControllerTests - { - private HttpClient client; - - public RestierControllerTests() - { - var configuration = new HttpConfiguration(); - configuration.MapRestierRoute("store", "store").Wait(); - client = new HttpClient(new HttpServer(configuration)); - } - - [Fact] - public async Task MetadataTest() - { - const string expected = @" - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -"; - var request = new HttpRequestMessage(HttpMethod.Get, "http://host/store/$metadata"); - request.Headers.Accept.Add(MediaTypeWithQualityHeaderValue.Parse("application/xml")); - var response = await client.SendAsync(request); - var result = await response.Content.ReadAsStringAsync(); - - Assert.Equal(HttpStatusCode.OK, response.StatusCode); - Assert.Equal(expected.Replace(" ", "").Replace("\r\n", ""), result.Replace(" ", "")); - } + [TestClass] + public class RestierControllerTests : RestierTestBase + { - [Fact] + [TestMethod] public async Task GetTest() { - var request = new HttpRequestMessage(HttpMethod.Get, "http://host/store/Products(1)"); - request.Headers.Accept.Add(MediaTypeWithQualityHeaderValue.Parse("application/json;odata.metadata=full")); - var response = await client.SendAsync(request); - Assert.Equal(HttpStatusCode.OK, response.StatusCode); + var response = await RestierTestHelpers.ExecuteTestRequest(HttpMethod.Get, resource: "/Products(1)"); + var content = await response.Content.ReadAsStringAsync(); + TestContext.WriteLine(content); + response.IsSuccessStatusCode.Should().BeTrue(); } - [Fact] + [TestMethod] public async Task GetNonExistingEntityTest() { - var request = new HttpRequestMessage(HttpMethod.Get, "http://host/store/Products(-1)"); - request.Headers.Accept.Add(MediaTypeWithQualityHeaderValue.Parse("application/json;odata.metadata=full")); - var response = await client.SendAsync(request); - Assert.Equal(HttpStatusCode.NotFound, response.StatusCode); + var response = await RestierTestHelpers.ExecuteTestRequest(HttpMethod.Get, resource: "/Products(-1)"); + var content = await response.Content.ReadAsStringAsync(); + TestContext.WriteLine(content); + response.StatusCode.Should().Be(HttpStatusCode.NotFound); } - [Fact] + [TestMethod] public async Task PostTest() { - const string payload = "{'Name': 'var1', 'Addr':{'Zip':330}}"; - var request = new HttpRequestMessage(HttpMethod.Post, "http://host/store/Products") + var payload = new { - Content = new StringContent(payload, Encoding.UTF8, "application/json") + Name = "var1", + Addr = new Address { Zip = 330 } }; - var response = await client.SendAsync(request); - Assert.Equal(HttpStatusCode.Created, response.StatusCode); + var response = await RestierTestHelpers.ExecuteTestRequest(HttpMethod.Post, resource: "/Products", payload: payload, acceptHeader: WebApiConstants.DefaultAcceptHeader); + var content = await response.Content.ReadAsStringAsync(); + TestContext.WriteLine(content); + response.StatusCode.Should().Be(HttpStatusCode.Created); } - [Fact] + [TestMethod] public async Task FunctionImportNotInModelShouldReturnNotFound() { - var request = new HttpRequestMessage(HttpMethod.Get, "http://host/store/GetBestProduct2"); - request.Headers.Accept.Add(MediaTypeWithQualityHeaderValue.Parse("application/json")); - var response = await client.SendAsync(request); - Assert.Equal(HttpStatusCode.NotFound, response.StatusCode); + var response = await RestierTestHelpers.ExecuteTestRequest(HttpMethod.Get, resource: "/GetBestProduct2"); + var content = await response.Content.ReadAsStringAsync(); + TestContext.WriteLine(content); + response.StatusCode.Should().Be(HttpStatusCode.NotFound); } - [Fact] + [TestMethod] public async Task FunctionImportNotInControllerShouldReturnNotImplemented() { - var request = new HttpRequestMessage(HttpMethod.Get, "http://host/store/GetBestProduct"); - request.Headers.Accept.Add(MediaTypeWithQualityHeaderValue.Parse("application/json")); - var response = await client.SendAsync(request); - Assert.Equal(HttpStatusCode.NotImplemented, response.StatusCode); + var response = await RestierTestHelpers.ExecuteTestRequest(HttpMethod.Get, resource: "/GetBestProduct"); + var content = await response.Content.ReadAsStringAsync(); + TestContext.WriteLine(content); + response.StatusCode.Should().Be(HttpStatusCode.NotImplemented); } - [Fact] + [TestMethod] public async Task ActionImportNotInModelShouldReturnNotFound() { - var request = new HttpRequestMessage(HttpMethod.Post, "http://host/store/RemoveWorstProduct2"); - request.Headers.Accept.Add(MediaTypeWithQualityHeaderValue.Parse("application/json")); - var response = await client.SendAsync(request); - Assert.Equal(HttpStatusCode.NotFound, response.StatusCode); + var response = await RestierTestHelpers.ExecuteTestRequest(HttpMethod.Get, resource: "/RemoveWorstProduct2"); + var content = await response.Content.ReadAsStringAsync(); + TestContext.WriteLine(content); + response.StatusCode.Should().Be(HttpStatusCode.NotFound); } - [Fact] + [TestMethod] public async Task ActionImportNotInControllerShouldReturnNotImplemented() { - var request = new HttpRequestMessage(HttpMethod.Post, "http://host/store/RemoveWorstProduct"); - request.Headers.Accept.Add(MediaTypeWithQualityHeaderValue.Parse("application/json")); - var response = await client.SendAsync(request); - // TODO standalone testing shows 501, but here is 500, will figure out detail reason - Assert.Equal(HttpStatusCode.InternalServerError, response.StatusCode); + var response = await RestierTestHelpers.ExecuteTestRequest(HttpMethod.Post, resource: "/RemoveWorstProduct"); + var content = await response.Content.ReadAsStringAsync(); + TestContext.WriteLine(content); + // TODO: standalone testing shows 501, but here is 500, will figure out detail reason + response.StatusCode.Should().Be(HttpStatusCode.InternalServerError); } - [Fact] + [TestMethod] public async Task GetActionImportShouldReturnNotFound() { - var request = new HttpRequestMessage(HttpMethod.Get, "http://host/store/RemoveWorstProduct"); - request.Headers.Accept.Add(MediaTypeWithQualityHeaderValue.Parse("application/json")); - var response = await client.SendAsync(request); - Assert.Equal(HttpStatusCode.NotFound, response.StatusCode); + var response = await RestierTestHelpers.ExecuteTestRequest(HttpMethod.Get, resource: "/RemoveWorstProduct"); + var content = await response.Content.ReadAsStringAsync(); + TestContext.WriteLine(content); + response.StatusCode.Should().Be(HttpStatusCode.NotFound); } - [Fact] + [TestMethod] public async Task PostFunctionImportShouldReturnNotFound() { - var request = new HttpRequestMessage(HttpMethod.Post, "http://host/store/GetBestProduct"); - request.Headers.Accept.Add(MediaTypeWithQualityHeaderValue.Parse("application/json")); - var response = await client.SendAsync(request); - Assert.Equal(HttpStatusCode.InternalServerError, response.StatusCode); + var response = await RestierTestHelpers.ExecuteTestRequest(HttpMethod.Post, resource: "/GetBestProduct"); + var content = await response.Content.ReadAsStringAsync(); + TestContext.WriteLine(content); + // TODO: standalone testing shows 501, but here is 500, will figure out detail reason + response.StatusCode.Should().Be(HttpStatusCode.InternalServerError); } + } -} + +} \ No newline at end of file diff --git a/src/Microsoft.Restier.Tests.AspNet/RestierQueryBuilderTests.cs b/src/Microsoft.Restier.Tests.AspNet/RestierQueryBuilderTests.cs index 83d85287..13daf67c 100644 --- a/src/Microsoft.Restier.Tests.AspNet/RestierQueryBuilderTests.cs +++ b/src/Microsoft.Restier.Tests.AspNet/RestierQueryBuilderTests.cs @@ -1,48 +1,34 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. See License.txt in the project root for license information. -using System.Diagnostics; -using System.Net; using System.Net.Http; -using System.Net.Http.Headers; using System.Threading.Tasks; -using System.Web.Http; -using Xunit; -using Xunit.Abstractions; +using CloudNimble.Breakdance.Restier; +using FluentAssertions; +using Microsoft.Restier.Tests.Shared; +using Microsoft.VisualStudio.TestTools.UnitTesting; namespace Microsoft.Restier.Tests.AspNet { - public class RestierQueryBuilderTests - { - - private HttpClient client; - private readonly ITestOutputHelper output; - public RestierQueryBuilderTests(ITestOutputHelper output) - { - var configuration = new HttpConfiguration(); - configuration.MapRestierRoute("store", "store").Wait(); - client = new HttpClient(new HttpServer(configuration)); - this.output = output; - } + [TestClass] + public class RestierQueryBuilderTests : RestierTestBase + { - [Fact] + [TestMethod] public async Task TestInt16AsKey() { - var request = new HttpRequestMessage(HttpMethod.Get, "http://host/store/Customers(1)"); - request.Headers.Accept.Add(MediaTypeWithQualityHeaderValue.Parse("application/json;odata.metadata=full")); - var response = await client.SendAsync(request); - output.WriteLine(await response.Content.ReadAsStringAsync()); - Assert.Equal(HttpStatusCode.OK, response.StatusCode); + var response = await RestierTestHelpers.ExecuteTestRequest(HttpMethod.Get, resource: "/Customers(1)"); + response.IsSuccessStatusCode.Should().BeTrue(); + TestContext.WriteLine(await response.Content.ReadAsStringAsync()); } - [Fact] + [TestMethod] public async Task TestInt64AsKey() { - var request = new HttpRequestMessage(HttpMethod.Get, "http://host/store/Stores(1)"); - request.Headers.Accept.Add(MediaTypeWithQualityHeaderValue.Parse("application/json;odata.metadata=full")); - var response = await client.SendAsync(request); - Assert.Equal(HttpStatusCode.OK, response.StatusCode); + var response = await RestierTestHelpers.ExecuteTestRequest(HttpMethod.Get, resource: "/Stores(1)"); + response.IsSuccessStatusCode.Should().BeTrue(); + TestContext.WriteLine(await response.Content.ReadAsStringAsync()); } } } diff --git a/src/Microsoft.Restier.Tests.AspNetCore/Microsoft.Restier.Tests.AspNetCore.csproj b/src/Microsoft.Restier.Tests.AspNetCore/Microsoft.Restier.Tests.AspNetCore.csproj index 2868cb73..60b78782 100644 --- a/src/Microsoft.Restier.Tests.AspNetCore/Microsoft.Restier.Tests.AspNetCore.csproj +++ b/src/Microsoft.Restier.Tests.AspNetCore/Microsoft.Restier.Tests.AspNetCore.csproj @@ -8,11 +8,8 @@ - - - all - runtime; build; native; contentfiles; analyzers - + + diff --git a/src/Microsoft.Restier.Tests.AspNetCore/UnitTest1.cs b/src/Microsoft.Restier.Tests.AspNetCore/UnitTest1.cs deleted file mode 100644 index bbafd308..00000000 --- a/src/Microsoft.Restier.Tests.AspNetCore/UnitTest1.cs +++ /dev/null @@ -1,14 +0,0 @@ -using System; -using Xunit; - -namespace Microsoft.Restier.Tests.AspNetCore -{ - public class UnitTest1 - { - [Fact] - public void Test1() - { - - } - } -} diff --git a/src/Microsoft.Restier.Tests.Core/ApiBaseTests.cs b/src/Microsoft.Restier.Tests.Core/ApiBaseTests.cs index 75b150ac..34b990e7 100644 --- a/src/Microsoft.Restier.Tests.Core/ApiBaseTests.cs +++ b/src/Microsoft.Restier.Tests.Core/ApiBaseTests.cs @@ -2,61 +2,353 @@ // Licensed under the MIT License. See License.txt in the project root for license information. using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using System.Linq.Expressions; +using System.Threading; +using System.Threading.Tasks; +using CloudNimble.Breakdance.Restier; +using FluentAssertions; using Microsoft.Extensions.DependencyInjection; -using Xunit; +using Microsoft.OData.Edm; +using Microsoft.Restier.Core; +using Microsoft.Restier.Core.Model; +using Microsoft.Restier.Core.Query; +using Microsoft.Restier.Core.Submit; +using Microsoft.Restier.Tests.Shared; +using Microsoft.VisualStudio.TestTools.UnitTesting; -namespace Microsoft.Restier.Core.Tests +namespace Microsoft.Restier.Tests.Core { - public class ApiBaseTests + + [TestClass] + public class ApiBaseTests : RestierTestBase { - private class TestApi : ApiBase + + [TestMethod] + public async Task DefaultApiBaseCanBeCreatedAndDisposed() { - public static new IServiceCollection ConfigureApi(Type apiType, IServiceCollection services) + var api = await RestierTestHelpers.GetTestableApiInstance(); + + Action exceptionTest = () => { api.Dispose(); }; + exceptionTest.Should().NotThrow(); + } + + #region EntitySets + + [TestMethod] + public async Task GetQueryableSource_EntitySet_IsConfiguredCorrectly() + { + var api = await RestierTestHelpers.GetTestableApiInstance() as ApiBase; + var arguments = new object[0]; + var source = api.GetQueryableSource("Test", arguments); + + CheckQueryable(source, typeof(string), new List { "Test" }, arguments); + } + + [TestMethod] + public async Task GetQueryableSource_OfT_EntitySet_IsConfiguredCorrectly() + { + var api = await RestierTestHelpers.GetTestableApiInstance() as ApiBase; + var arguments = new object[0]; + var source = api.GetQueryableSource("Test", arguments); + + CheckQueryable(source, typeof(string), new List { "Test" }, arguments); + } + + [TestMethod] + public async Task GetQueryableSource_EntitySet_ThrowsIfNotMapped() + { + var api = await RestierTestHelpers.GetTestableApiInstance() as ApiBase; + var arguments = new object[0]; + + Action exceptionTest = () => { api.GetQueryableSource("Test", arguments); }; + exceptionTest.Should().Throw(); + } + + [TestMethod] + public async Task GetQueryableSource_OfT_ContainerElementThrowsIfWrongType() + { + var api = await RestierTestHelpers.GetTestableApiInstance() as ApiBase; + var arguments = new object[0]; + + Action exceptionTest = () => { api.GetQueryableSource("Test", arguments); }; + exceptionTest.Should().Throw(); + + } + + #endregion + + #region Functions + + [TestMethod] + public async Task GetQueryableSource_ComposableFunction_IsConfiguredCorrectly() + { + var api = await RestierTestHelpers.GetTestableApiInstance() as ApiBase; + var arguments = new object[0]; + var source = api.GetQueryableSource("Namespace", "Function", arguments); + + CheckQueryable(source, typeof(DateTime), new List { "Namespace", "Function" }, arguments); + } + + [TestMethod] + public async Task GetQueryableSource_OfT_ComposableFunction_IsConfiguredCorrectly() + { + var api = await RestierTestHelpers.GetTestableApiInstance() as ApiBase; + var arguments = new object[0]; + var source = api.GetQueryableSource("Namespace", "Function", arguments); + + CheckQueryable(source, typeof(DateTime), new List { "Namespace", "Function" }, arguments); + } + + [TestMethod] + public async Task GetQueryableSource_ComposableFunction_ThrowsIfNotMapped() + { + var api = await RestierTestHelpers.GetTestableApiInstance() as ApiBase; + var arguments = new object[0]; + + Action exceptionTest = () => { api.GetQueryableSource("Namespace", "Function", arguments); }; + exceptionTest.Should().Throw(); + } + + [TestMethod] + public async Task GetQueryableSource_OfT_ComposableFunction_ThrowsIfNotMapped() + { + var api = await RestierTestHelpers.GetTestableApiInstance() as ApiBase; + var arguments = new object[0]; + + Action exceptionTest = () => { api.GetQueryableSource("Namespace", "Function", arguments); }; + exceptionTest.Should().Throw(); + } + + [TestMethod] + public async Task GetQueryableSource_ComposableFunction_ThrowsIfWrongType() + { + var api = await RestierTestHelpers.GetTestableApiInstance() as ApiBase; + var arguments = new object[0]; + + Action exceptionTest = () => { api.GetQueryableSource("Namespace", "Function", arguments); }; + exceptionTest.Should().Throw(); + + } + + #endregion + + #region QueryAsync + + [TestMethod] + public async Task QueryAsync_WithQueryReturnsResults() + { + var api = await RestierTestHelpers.GetTestableApiInstance() as ApiBase; + + var request = new QueryRequest(api.GetQueryableSource("Test")); + var result = await api.QueryAsync(request); + var results = result.Results.Cast(); + + results.SequenceEqual(new[] {"Test"}).Should().BeTrue(); + } + + [TestMethod] + public async Task QueryAsync_CorrectlyForwardsCall() + { + var api = await RestierTestHelpers.GetTestableApiInstance() as ApiBase; + var queryRequest = new QueryRequest(api.GetQueryableSource("Test")); + var queryResult = await api.QueryAsync(queryRequest); + + queryResult.Results.Cast().SequenceEqual(new[] { "Test" }).Should().BeTrue(); + } + + #endregion + + #region SubmitAsync + + [TestMethod] + public async Task SubmitAsync_CorrectlyForwardsCall() + { + var api = await RestierTestHelpers.GetTestableApiInstance() as ApiBase; + var submitResult = await api.SubmitAsync(); + + submitResult.CompletedChangeSet.Should().NotBeNull(); + } + + #endregion + + #region Exceptions + + [TestMethod] + public async Task GetQueryableSource_CannotEnumerate() + { + var api = await RestierTestHelpers.GetTestableApiInstance() as ApiBase; + var source = api.GetQueryableSource("Test"); + + Action exceptionTest = () => { source.GetEnumerator(); }; + exceptionTest.Should().Throw(); + + } + + [TestMethod] + public async Task GetQueryableSource_CannotEnumerateIEnumerable() + { + var api = await RestierTestHelpers.GetTestableApiInstance() as ApiBase; + var source = api.GetQueryableSource("Test"); + + Action exceptionTest = () => { (source as IEnumerable).GetEnumerator(); }; + exceptionTest.Should().Throw(); + } + + [TestMethod] + public async Task GetQueryableSource_ProviderCannotGenericExecute() + { + var api = await RestierTestHelpers.GetTestableApiInstance() as ApiBase; + var source = api.GetQueryableSource("Test"); + + Action exceptionTest = () => { source.Provider.Execute(null); }; + exceptionTest.Should().Throw(); + + } + + [TestMethod] + public async Task GetQueryableSource_ProviderCannotExecute() + { + var api = await RestierTestHelpers.GetTestableApiInstance() as ApiBase; + var source = api.GetQueryableSource("Test"); + + Action exceptionTest = () => { source.Provider.Execute(null); }; + exceptionTest.Should().Throw(); + } + + #endregion + + #region Helpers + + /// + /// Runs a set of checks against an IQueryable to make sure it has been processed properly. + /// + /// The or to test. + /// The returned by the . + /// A containing the parts of the expression to check for. + /// An array of arguments that the we're testing requires. RWM: In the tests, this is an empty array. Not sure if that is v alid or not. + public void CheckQueryable(IQueryable source, Type elementType, List expressionValues, object[] arguments) + { + source.ElementType.Should().Be(elementType); + (source.Expression is MethodCallExpression).Should().BeTrue(); + var methodCall = source.Expression as MethodCallExpression; + methodCall.Object.Should().BeNull(); + methodCall.Method.DeclaringType.Should().Be(typeof(DataSourceStub)); + methodCall.Method.Name.Should().Be("GetQueryableSource"); + methodCall.Method.GetGenericArguments()[0].Should().Be(elementType); + methodCall.Arguments.Should().HaveCount(expressionValues.Count + 1); + + for (var i = 0; i < expressionValues.Count; i++) { - return ApiBase.ConfigureApi(apiType, services) - .MakeScoped() - .AddService(); + (methodCall.Arguments[i] is ConstantExpression).Should().BeTrue(); + (methodCall.Arguments[i] as ConstantExpression).Value.Should().Be(expressionValues[i]); + source.ToString().Should().Be(source.Expression.ToString()); } - public TestApi(IServiceProvider serviceProvider) : base(serviceProvider) + (methodCall.Arguments[expressionValues.Count] is ConstantExpression).Should().BeTrue(); + (methodCall.Arguments[expressionValues.Count] as ConstantExpression).Value.Should().Be(arguments); + source.ToString().Should().Be(source.Expression.ToString()); + + } + + #endregion + + #region Test Resources + + private class TestModelBuilder : IModelBuilder + { + public Task GetModelAsync(ModelContext context, CancellationToken cancellationToken) { + var model = new EdmModel(); + var dummyType = new EdmEntityType("NS", "Dummy"); + model.AddElement(dummyType); + var container = new EdmEntityContainer("NS", "DefaultContainer"); + container.AddEntitySet("Test", dummyType); + model.AddElement(container); + return Task.FromResult((IEdmModel)model); } } - interface IService + private class TestModelMapper : IModelMapper { - ApiBase Api { get; } + public bool TryGetRelevantType( + ModelContext context, + string name, out Type relevantType) + { + relevantType = typeof(string); + return true; + } + + public bool TryGetRelevantType( + ModelContext context, + string namespaceName, string name, + out Type relevantType) + { + relevantType = typeof(DateTime); + return true; + } } - class Service : IService + private class TestQuerySourcer : IQueryExpressionSourcer { - public ApiBase Api { get; set; } + public Expression ReplaceQueryableSource(QueryExpressionContext context, bool embedded) + { + return Expression.Constant(new[] { "Test" }.AsQueryable()); + } + } - public Service(ApiBase api) + private class TestChangeSetInitializer : IChangeSetInitializer + { + public Task InitializeAsync(SubmitContext context, CancellationToken cancellationToken) { - Api = api; + context.ChangeSet = new ChangeSet(); + return Task.FromResult(null); } } - [Fact] - public void DefaultApiBaseCanBeCreatedAndDisposed() + private class TestSubmitExecutor : ISubmitExecutor { - var container = new RestierContainerBuilder(typeof(TestApi)); - var provider = container.BuildContainer(); - var api = provider.GetService(); - api.Dispose(); + public Task ExecuteSubmitAsync(SubmitContext context, CancellationToken cancellationToken) + { + return Task.FromResult(new SubmitResult(context.ChangeSet)); + } } - [Fact] - public void ApiAndApiContextCanBeInjectedByDI() + private class TestApi : ApiBase { - var container = new RestierContainerBuilder(typeof(TestApi)); - var provider = container.BuildContainer(); - var api = provider.GetService(); + public static new IServiceCollection ConfigureApi(Type apiType, IServiceCollection services) + { + var modelBuilder = new TestModelBuilder(); + var modelMapper = new TestModelMapper(); + var querySourcer = new TestQuerySourcer(); + var changeSetPreparer = new TestChangeSetInitializer(); + var submitExecutor = new TestSubmitExecutor(); + + services.AddCoreServices(apiType); + services.AddService((sp, next) => modelBuilder); + services.AddService((sp, next) => modelMapper); + services.AddService((sp, next) => querySourcer); + services.AddService((sp, next) => changeSetPreparer); + services.AddService((sp, next) => submitExecutor); - var svc = api.GetApiService(); + return services; + } - Assert.Same(svc.Api, api); + public TestApi(IServiceProvider serviceProvider) : base(serviceProvider) + { + } } + + private class TestApiEmpty : ApiBase + { + public TestApiEmpty(IServiceProvider serviceProvider) : base(serviceProvider) + { + } + } + + #endregion + } } diff --git a/src/Microsoft.Restier.Tests.Core/ApiConfigurationTests.cs b/src/Microsoft.Restier.Tests.Core/ApiConfigurationTests.cs index 4f4dd065..bd7341e3 100644 --- a/src/Microsoft.Restier.Tests.Core/ApiConfigurationTests.cs +++ b/src/Microsoft.Restier.Tests.Core/ApiConfigurationTests.cs @@ -5,46 +5,44 @@ using System.Text; using System.Threading; using System.Threading.Tasks; +using CloudNimble.Breakdance.Restier; +using FluentAssertions; using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.DependencyInjection.Extensions; using Microsoft.OData.Edm; +using Microsoft.Restier.Core; using Microsoft.Restier.Core.Model; -using Xunit; +using Microsoft.Restier.Tests.Shared; +using Microsoft.VisualStudio.TestTools.UnitTesting; -namespace Microsoft.Restier.Core.Tests +namespace Microsoft.Restier.Tests.Core { - public class ApiConfigurationTests + + [TestClass] + public class ApiConfigurationTests : RestierTestBase { - [Fact] - public void ConfigurationRegistersApiServicesCorrectly() + [TestMethod] + public async Task ConfigurationRegistersApiServicesCorrectly() { - var container = new RestierContainerBuilder(typeof(TestApiA)); - var provider = container.BuildContainer(); - var api = provider.GetService(); + var apiA = await RestierTestHelpers.GetTestableApiInstance(); - Assert.Null(api.GetApiService()); - Assert.Null(api.GetApiService()); + apiA.GetApiService().Should().BeNull(); + apiA.GetApiService().Should().BeNull(); - container = new RestierContainerBuilder(typeof(TestApiB)); - var provider2 = container.BuildContainer(); - var apiB = provider2.GetService(); + var apiB = await RestierTestHelpers.GetTestableApiInstance(); - Assert.Same(TestApiB.serviceA, apiB.GetApiService()); + apiB.GetApiService().Should().BeSameAs(TestApiB.serviceA); var serviceBInstance = apiB.GetApiService(); var serviceBInterface = apiB.GetApiService(); - Assert.Equal(serviceBInstance, serviceBInterface); - - // AddService will call services.TryAddTransient - Assert.Same(serviceBInstance, serviceBInterface); + serviceBInterface.Should().BeSameAs(serviceBInstance); var serviceBFirst = serviceBInterface as ServiceB; - Assert.NotNull(serviceBFirst); + serviceBFirst.Should().NotBeNull(); - Assert.Same(TestApiB.serviceB, serviceBFirst.InnerHandler); + serviceBFirst.InnerHandler.Should().BeSameAs(TestApiB.serviceB); } - [Fact] + [TestMethod] public void ServiceChainTest() { var container = new RestierContainerBuilder(typeof(TestApiC)); @@ -52,7 +50,7 @@ public void ServiceChainTest() var api = provider.GetService(); var handler = api.GetApiService(); - Assert.Equal("q2Pre_q1Pre_q1Post_q2Post_", handler.GetStr()); + handler.GetStr().Should().Be("q2Pre_q1Pre_q1Post_q2Post_"); } private class TestApiA : ApiBase diff --git a/src/Microsoft.Restier.Tests.Core/ApiContextTests.cs b/src/Microsoft.Restier.Tests.Core/ApiContextTests.cs deleted file mode 100644 index 0f27420a..00000000 --- a/src/Microsoft.Restier.Tests.Core/ApiContextTests.cs +++ /dev/null @@ -1,29 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. See License.txt in the project root for license information. - -using System; -using Microsoft.Extensions.DependencyInjection; -using Xunit; - -namespace Microsoft.Restier.Core.Tests -{ - public class ApiContextTests - { - private class TestApi : ApiBase - { - public TestApi(IServiceProvider serviceProvider) : base(serviceProvider) - { - } - } - - [Fact] - public void NewApiContextIsConfiguredCorrectly() - { - var container = new RestierContainerBuilder(typeof(TestApi)); - var provider = container.BuildContainer(); - var api = provider.GetService(); - - Assert.NotNull(api); - } - } -} diff --git a/src/Microsoft.Restier.Tests.Core/ApiTests.cs b/src/Microsoft.Restier.Tests.Core/ApiTests.cs deleted file mode 100644 index 22bbc703..00000000 --- a/src/Microsoft.Restier.Tests.Core/ApiTests.cs +++ /dev/null @@ -1,449 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. See License.txt in the project root for license information. - -using System; -using System.Collections; -using System.Linq; -using System.Linq.Expressions; -using System.Net; -using System.Threading; -using System.Threading.Tasks; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.OData.Edm; -using Microsoft.Restier.Core.Model; -using Microsoft.Restier.Core.Query; -using Microsoft.Restier.Core.Submit; -using Xunit; - -namespace Microsoft.Restier.Core.Tests -{ - public class ApiTests - { - private class TestModelBuilder : IModelBuilder - { - public Task GetModelAsync(ModelContext context, CancellationToken cancellationToken) - { - var model = new EdmModel(); - var dummyType = new EdmEntityType("NS", "Dummy"); - model.AddElement(dummyType); - var container = new EdmEntityContainer("NS", "DefaultContainer"); - container.AddEntitySet("Test", dummyType); - model.AddElement(container); - return Task.FromResult((IEdmModel) model); - } - } - - private class TestModelMapper : IModelMapper - { - public bool TryGetRelevantType( - ModelContext context, - string name, out Type relevantType) - { - relevantType = typeof(string); - return true; - } - - public bool TryGetRelevantType( - ModelContext context, - string namespaceName, string name, - out Type relevantType) - { - relevantType = typeof(DateTime); - return true; - } - } - - private class TestQuerySourcer : IQueryExpressionSourcer - { - public Expression ReplaceQueryableSource(QueryExpressionContext context, bool embedded) - { - return Expression.Constant(new[] {"Test"}.AsQueryable()); - } - } - - private class TestChangeSetInitializer : IChangeSetInitializer - { - public Task InitializeAsync(SubmitContext context, CancellationToken cancellationToken) - { - context.ChangeSet = new ChangeSet(); - return Task.FromResult(null); - } - } - - private class TestSubmitExecutor : ISubmitExecutor - { - public Task ExecuteSubmitAsync(SubmitContext context, CancellationToken cancellationToken) - { - return Task.FromResult(new SubmitResult(context.ChangeSet)); - } - } - - private class TestApi : ApiBase - { - public static new IServiceCollection ConfigureApi(Type apiType, IServiceCollection services) - { - var modelBuilder = new TestModelBuilder(); - var modelMapper = new TestModelMapper(); - var querySourcer = new TestQuerySourcer(); - var changeSetPreparer = new TestChangeSetInitializer(); - var submitExecutor = new TestSubmitExecutor(); - - services.AddCoreServices(apiType); - services.AddService((sp, next) => modelBuilder); - services.AddService((sp, next) => modelMapper); - services.AddService((sp, next) => querySourcer); - services.AddService((sp, next) => changeSetPreparer); - services.AddService((sp, next) => submitExecutor); - - return services; - } - - public TestApi(IServiceProvider serviceProvider) : base(serviceProvider) - { - } - } - - private class TestApiEmpty : ApiBase - { - public TestApiEmpty(IServiceProvider serviceProvider) : base(serviceProvider) - { - } - } - - [Fact] - public void ApiSourceOfEntityContainerElementIsCorrect() - { - var container = new RestierContainerBuilder(typeof(TestApi)); - var provider = container.BuildContainer(); - var api = provider.GetService(); - - var arguments = new object[0]; - - var source = api.GetQueryableSource("Test", arguments); - Assert.Equal(typeof(string), source.ElementType); - Assert.True(source.Expression is MethodCallExpression); - var methodCall = source.Expression as MethodCallExpression; - Assert.Null(methodCall.Object); - Assert.Equal(typeof(DataSourceStub), methodCall.Method.DeclaringType); - Assert.Equal("GetQueryableSource", methodCall.Method.Name); - Assert.Equal(typeof(string), methodCall.Method.GetGenericArguments()[0]); - Assert.Equal(2, methodCall.Arguments.Count); - Assert.True(methodCall.Arguments[0] is ConstantExpression); - Assert.Equal("Test", (methodCall.Arguments[0] as ConstantExpression).Value); - Assert.True(methodCall.Arguments[1] is ConstantExpression); - Assert.Equal(arguments, (methodCall.Arguments[1] as ConstantExpression).Value); - Assert.Equal(source.Expression.ToString(), source.ToString()); - } - - [Fact] - public void SourceOfEntityContainerElementThrowsIfNotMapped() - { - var container = new RestierContainerBuilder(typeof(TestApiEmpty)); - var provider = container.BuildContainer(); - var api = provider.GetService(); - var arguments = new object[0]; - - Assert.Throws(() => api.GetQueryableSource("Test", arguments)); - } - - [Fact] - public void SourceOfEntityContainerElementIsCorrect() - { - var container = new RestierContainerBuilder(typeof(TestApi)); - var provider = container.BuildContainer(); - var api = provider.GetService(); - var arguments = new object[0]; - - var source = api.GetQueryableSource("Test", arguments); - Assert.Equal(typeof(string), source.ElementType); - Assert.True(source.Expression is MethodCallExpression); - var methodCall = source.Expression as MethodCallExpression; - Assert.Null(methodCall.Object); - Assert.Equal(typeof(DataSourceStub), methodCall.Method.DeclaringType); - Assert.Equal("GetQueryableSource", methodCall.Method.Name); - Assert.Equal(typeof(string), methodCall.Method.GetGenericArguments()[0]); - Assert.Equal(2, methodCall.Arguments.Count); - Assert.True(methodCall.Arguments[0] is ConstantExpression); - Assert.Equal("Test", (methodCall.Arguments[0] as ConstantExpression).Value); - Assert.True(methodCall.Arguments[1] is ConstantExpression); - Assert.Equal(arguments, (methodCall.Arguments[1] as ConstantExpression).Value); - Assert.Equal(source.Expression.ToString(), source.ToString()); - } - - [Fact] - public void ApiSourceOfComposableFunctionIsCorrect() - { - var container = new RestierContainerBuilder(typeof(TestApi)); - var provider = container.BuildContainer(); - var api = provider.GetService(); - - var arguments = new object[0]; - - var source = api.GetQueryableSource("Namespace", "Function", arguments); - Assert.Equal(typeof(DateTime), source.ElementType); - Assert.True(source.Expression is MethodCallExpression); - var methodCall = source.Expression as MethodCallExpression; - Assert.Null(methodCall.Object); - Assert.Equal(typeof(DataSourceStub), methodCall.Method.DeclaringType); - Assert.Equal("GetQueryableSource", methodCall.Method.Name); - Assert.Equal(typeof(DateTime), methodCall.Method.GetGenericArguments()[0]); - Assert.Equal(3, methodCall.Arguments.Count); - Assert.True(methodCall.Arguments[0] is ConstantExpression); - Assert.Equal("Namespace", (methodCall.Arguments[0] as ConstantExpression).Value); - Assert.True(methodCall.Arguments[1] is ConstantExpression); - Assert.Equal("Function", (methodCall.Arguments[1] as ConstantExpression).Value); - Assert.True(methodCall.Arguments[2] is ConstantExpression); - Assert.Equal(arguments, (methodCall.Arguments[2] as ConstantExpression).Value); - Assert.Equal(source.Expression.ToString(), source.ToString()); - } - - [Fact] - public void SourceOfComposableFunctionThrowsIfNotMapped() - { - var container = new RestierContainerBuilder(typeof(TestApiEmpty)); - var provider = container.BuildContainer(); - var api = provider.GetService(); - var arguments = new object[0]; - - Assert.Throws(() => api.GetQueryableSource("Namespace", "Function", arguments)); - } - - [Fact] - public void SourceOfComposableFunctionIsCorrect() - { - var container = new RestierContainerBuilder(typeof(TestApi)); - var provider = container.BuildContainer(); - var api = provider.GetService(); - - var arguments = new object[0]; - - var source = api.GetQueryableSource("Namespace", "Function", arguments); - Assert.Equal(typeof(DateTime), source.ElementType); - Assert.True(source.Expression is MethodCallExpression); - var methodCall = source.Expression as MethodCallExpression; - Assert.Null(methodCall.Object); - Assert.Equal(typeof(DataSourceStub), methodCall.Method.DeclaringType); - Assert.Equal("GetQueryableSource", methodCall.Method.Name); - Assert.Equal(typeof(DateTime), methodCall.Method.GetGenericArguments()[0]); - Assert.Equal(3, methodCall.Arguments.Count); - Assert.True(methodCall.Arguments[0] is ConstantExpression); - Assert.Equal("Namespace", (methodCall.Arguments[0] as ConstantExpression).Value); - Assert.True(methodCall.Arguments[1] is ConstantExpression); - Assert.Equal("Function", (methodCall.Arguments[1] as ConstantExpression).Value); - Assert.True(methodCall.Arguments[2] is ConstantExpression); - Assert.Equal(arguments, (methodCall.Arguments[2] as ConstantExpression).Value); - Assert.Equal(source.Expression.ToString(), source.ToString()); - } - - [Fact] - public void GenericApiSourceOfEntityContainerElementIsCorrect() - { - var container = new RestierContainerBuilder(typeof(TestApi)); - var provider = container.BuildContainer(); - var api = provider.GetService(); - - var arguments = new object[0]; - - var source = api.GetQueryableSource("Test", arguments); - Assert.Equal(typeof(string), source.ElementType); - Assert.True(source.Expression is MethodCallExpression); - var methodCall = source.Expression as MethodCallExpression; - Assert.Null(methodCall.Object); - Assert.Equal(typeof(DataSourceStub), methodCall.Method.DeclaringType); - Assert.Equal("GetQueryableSource", methodCall.Method.Name); - Assert.Equal(typeof(string), methodCall.Method.GetGenericArguments()[0]); - Assert.Equal(2, methodCall.Arguments.Count); - Assert.True(methodCall.Arguments[0] is ConstantExpression); - Assert.Equal("Test", (methodCall.Arguments[0] as ConstantExpression).Value); - Assert.True(methodCall.Arguments[1] is ConstantExpression); - Assert.Equal(arguments, (methodCall.Arguments[1] as ConstantExpression).Value); - Assert.Equal(source.Expression.ToString(), source.ToString()); - } - - [Fact] - public void GenericSourceOfEntityContainerElementThrowsIfWrongType() - { - var container = new RestierContainerBuilder(typeof(TestApi)); - var provider = container.BuildContainer(); - var api = provider.GetService(); - var arguments = new object[0]; - - Assert.Throws(() => api.GetQueryableSource("Test", arguments)); - } - - [Fact] - public void GenericSourceOfEntityContainerElementIsCorrect() - { - var container = new RestierContainerBuilder(typeof(TestApi)); - var provider = container.BuildContainer(); - var api = provider.GetService(); - - var arguments = new object[0]; - - var source = api.GetQueryableSource("Test", arguments); - Assert.Equal(typeof(string), source.ElementType); - Assert.True(source.Expression is MethodCallExpression); - var methodCall = source.Expression as MethodCallExpression; - Assert.Null(methodCall.Object); - Assert.Equal(typeof(DataSourceStub), methodCall.Method.DeclaringType); - Assert.Equal("GetQueryableSource", methodCall.Method.Name); - Assert.Equal(typeof(string), methodCall.Method.GetGenericArguments()[0]); - Assert.Equal(2, methodCall.Arguments.Count); - Assert.True(methodCall.Arguments[0] is ConstantExpression); - Assert.Equal("Test", (methodCall.Arguments[0] as ConstantExpression).Value); - Assert.True(methodCall.Arguments[1] is ConstantExpression); - Assert.Equal(arguments, (methodCall.Arguments[1] as ConstantExpression).Value); - Assert.Equal(source.Expression.ToString(), source.ToString()); - } - - [Fact] - public void GenericApiSourceOfComposableFunctionIsCorrect() - { - var container = new RestierContainerBuilder(typeof(TestApi)); - var provider = container.BuildContainer(); - var api = provider.GetService(); - - var arguments = new object[0]; - - var source = api.GetQueryableSource( - "Namespace", "Function", arguments); - Assert.Equal(typeof(DateTime), source.ElementType); - Assert.True(source.Expression is MethodCallExpression); - var methodCall = source.Expression as MethodCallExpression; - Assert.Null(methodCall.Object); - Assert.Equal(typeof(DataSourceStub), methodCall.Method.DeclaringType); - Assert.Equal("GetQueryableSource", methodCall.Method.Name); - Assert.Equal(typeof(DateTime), methodCall.Method.GetGenericArguments()[0]); - Assert.Equal(3, methodCall.Arguments.Count); - Assert.True(methodCall.Arguments[0] is ConstantExpression); - Assert.Equal("Namespace", (methodCall.Arguments[0] as ConstantExpression).Value); - Assert.True(methodCall.Arguments[1] is ConstantExpression); - Assert.Equal("Function", (methodCall.Arguments[1] as ConstantExpression).Value); - Assert.True(methodCall.Arguments[2] is ConstantExpression); - Assert.Equal(arguments, (methodCall.Arguments[2] as ConstantExpression).Value); - Assert.Equal(source.Expression.ToString(), source.ToString()); - } - - [Fact] - public void GenericSourceOfComposableFunctionThrowsIfWrongType() - { - var container = new RestierContainerBuilder(typeof(TestApi)); - var provider = container.BuildContainer(); - var api = provider.GetService(); - - var arguments = new object[0]; - - Assert.Throws(() => api.GetQueryableSource("Namespace", "Function", arguments)); - } - - [Fact] - public void GenericSourceOfComposableFunctionIsCorrect() - { - var container = new RestierContainerBuilder(typeof(TestApi)); - var provider = container.BuildContainer(); - var api = provider.GetService(); - - var arguments = new object[0]; - - var source = api.GetQueryableSource("Namespace", "Function", arguments); - Assert.Equal(typeof(DateTime), source.ElementType); - Assert.True(source.Expression is MethodCallExpression); - var methodCall = source.Expression as MethodCallExpression; - Assert.Null(methodCall.Object); - Assert.Equal(typeof(DataSourceStub), methodCall.Method.DeclaringType); - Assert.Equal("GetQueryableSource", methodCall.Method.Name); - Assert.Equal(typeof(DateTime), methodCall.Method.GetGenericArguments()[0]); - Assert.Equal(3, methodCall.Arguments.Count); - Assert.True(methodCall.Arguments[0] is ConstantExpression); - Assert.Equal("Namespace", (methodCall.Arguments[0] as ConstantExpression).Value); - Assert.True(methodCall.Arguments[1] is ConstantExpression); - Assert.Equal("Function", (methodCall.Arguments[1] as ConstantExpression).Value); - Assert.True(methodCall.Arguments[2] is ConstantExpression); - Assert.Equal(arguments, (methodCall.Arguments[2] as ConstantExpression).Value); - Assert.Equal(source.Expression.ToString(), source.ToString()); - } - - [Fact] - public void SourceQueryableCannotGenericEnumerate() - { - var container = new RestierContainerBuilder(typeof(TestApi)); - var provider = container.BuildContainer(); - var api = provider.GetService(); - - var source = api.GetQueryableSource("Test"); - Assert.Throws(() => source.GetEnumerator()); - } - - [Fact] - public void SourceQueryableCannotEnumerate() - { - var container = new RestierContainerBuilder(typeof(TestApi)); - var provider = container.BuildContainer(); - var api = provider.GetService(); - - var source = api.GetQueryableSource("Test"); - Assert.Throws(() => (source as IEnumerable).GetEnumerator()); - } - - [Fact] - public void SourceQueryProviderCannotGenericExecute() - { - var container = new RestierContainerBuilder(typeof(TestApi)); - var provider = container.BuildContainer(); - var api = provider.GetService(); - - var source = api.GetQueryableSource("Test"); - Assert.Throws(() => source.Provider.Execute(null)); - } - - [Fact] - public void SourceQueryProviderCannotExecute() - { - var container = new RestierContainerBuilder(typeof(TestApi)); - var provider = container.BuildContainer(); - var api = provider.GetService(); - - var source = api.GetQueryableSource("Test"); - Assert.Throws(() => source.Provider.Execute(null)); - } - - [Fact] - public async Task ApiQueryAsyncWithQueryReturnsResults() - { - var container = new RestierContainerBuilder(typeof(TestApi)); - var provider = container.BuildContainer(); - var api = provider.GetService(); - - var request = new QueryRequest(api.GetQueryableSource("Test")); - var result = await api.QueryAsync(request); - var results = result.Results.Cast(); - - Assert.True(results.SequenceEqual(new[] {"Test"})); - } - - [Fact] - public async Task ApiQueryAsyncCorrectlyForwardsCall() - { - var container = new RestierContainerBuilder(typeof(TestApi)); - var provider = container.BuildContainer(); - var api = provider.GetService(); - - var queryRequest = new QueryRequest( - api.GetQueryableSource("Test")); - var queryResult = await api.QueryAsync(queryRequest); - Assert.True(queryResult.Results.Cast() - .SequenceEqual(new[] {"Test"})); - } - - [Fact] - public async Task ApiSubmitAsyncCorrectlyForwardsCall() - { - var container = new RestierContainerBuilder(typeof(TestApi)); - var provider = container.BuildContainer(); - var api = provider.GetService(); - - var submitResult = await api.SubmitAsync(); - Assert.NotNull(submitResult.CompletedChangeSet); - } - } -} diff --git a/src/Microsoft.Restier.Tests.Core/ConventionBasedMethodNameFactoryTests.cs b/src/Microsoft.Restier.Tests.Core/ConventionBasedMethodNameFactoryTests.cs index 577267e5..8af0ef36 100644 --- a/src/Microsoft.Restier.Tests.Core/ConventionBasedMethodNameFactoryTests.cs +++ b/src/Microsoft.Restier.Tests.Core/ConventionBasedMethodNameFactoryTests.cs @@ -2,89 +2,93 @@ // Licensed under the MIT License. See License.txt in the project root for license information. using System; -using Microsoft.Extensions.DependencyInjection; +using FluentAssertions; +using Microsoft.Restier.Core; using Microsoft.Restier.Core.Operation; using Microsoft.Restier.Core.Submit; -using Xunit; +using Microsoft.Restier.Tests.Shared; +using Microsoft.VisualStudio.TestTools.UnitTesting; -namespace Microsoft.Restier.Core.Tests +namespace Microsoft.Restier.Tests.Core { - public class ConventionBasedMethodNameFactoryTests + + [TestClass] + public class ConventionBasedMethodNameFactoryTests : RestierTestBase { - [Fact] + [TestMethod] public void ConventionBasedMethodNameFactory_Insert_PreSubmit() { var item = new DataModificationItem("TestItems", typeof(string), typeof(string), RestierEntitySetOperation.Insert, null, null, null); var name = ConventionBasedMethodNameFactory.GetEntitySetMethodName(item, RestierPipelineState.PreSubmit); - Assert.Equal("OnInsertingString", name); + name.Should().Be("OnInsertingString"); } - [Fact] + [TestMethod] public void ConventionBasedMethodNameFactory_Insert_PostSubmit() { var item = new DataModificationItem("TestItems", typeof(string), typeof(string), RestierEntitySetOperation.Insert, null, null, null); var name = ConventionBasedMethodNameFactory.GetEntitySetMethodName(item, RestierPipelineState.PostSubmit); - Assert.Equal("OnInsertedString", name); + name.Should().Be("OnInsertedString"); } - [Fact] + [TestMethod] public void ConventionBasedMethodNameFactory_Update_PreSubmit() { var item = new DataModificationItem("TestItems", typeof(string), typeof(string), RestierEntitySetOperation.Update, null, null, null); var name = ConventionBasedMethodNameFactory.GetEntitySetMethodName(item, RestierPipelineState.PreSubmit); - Assert.Equal("OnUpdatingString", name); + name.Should().Be("OnUpdatingString"); } - [Fact] + [TestMethod] public void ConventionBasedMethodNameFactory_Update_PostSubmit() { var item = new DataModificationItem("TestItems", typeof(string), typeof(string), RestierEntitySetOperation.Update, null, null, null); var name = ConventionBasedMethodNameFactory.GetEntitySetMethodName(item, RestierPipelineState.PostSubmit); - Assert.Equal("OnUpdatedString", name); + name.Should().Be("OnUpdatedString"); } - [Fact] + [TestMethod] public void ConventionBasedMethodNameFactory_Delete_PreSubmit() { var item = new DataModificationItem("TestItems", typeof(string), typeof(string), RestierEntitySetOperation.Delete, null, null, null); var name = ConventionBasedMethodNameFactory.GetEntitySetMethodName(item, RestierPipelineState.PreSubmit); - Assert.Equal("OnDeletingString", name); + name.Should().Be("OnDeletingString"); } - [Fact] + [TestMethod] public void ConventionBasedMethodNameFactory_Delete_PostSubmit() { var item = new DataModificationItem("TestItems", typeof(string), typeof(string), RestierEntitySetOperation.Delete, null, null, null); var name = ConventionBasedMethodNameFactory.GetEntitySetMethodName(item, RestierPipelineState.PostSubmit); - Assert.Equal("OnDeletedString", name); + name.Should().Be("OnDeletedString"); } - [Fact] + [TestMethod] public void ConventionBasedMethodNameFactory_Filter_PreSubmit() { var item = new DataModificationItem("TestItems", typeof(string), typeof(string), RestierEntitySetOperation.Filter, null, null, null); var name = ConventionBasedMethodNameFactory.GetEntitySetMethodName(item, RestierPipelineState.PreSubmit); - Assert.Equal("", name); + name.Should().Be(""); } - [Fact] + [TestMethod] public void ConventionBasedMethodNameFactory_Filter_Submit() { var item = new DataModificationItem("TestItems", typeof(string), typeof(string), RestierEntitySetOperation.Filter, null, null, null); var name = ConventionBasedMethodNameFactory.GetEntitySetMethodName(item, RestierPipelineState.Submit); - Assert.Equal("OnFilterTestItems", name); + name.Should().Be("OnFilterTestItems"); } - [Fact] + [TestMethod] public void ConventionBasedMethodNameFactory_Filter_PostSubmit() { var item = new DataModificationItem("TestItems", typeof(string), typeof(string), RestierEntitySetOperation.Filter, null, null, null); var name = ConventionBasedMethodNameFactory.GetEntitySetMethodName(item, RestierPipelineState.PostSubmit); - Assert.Equal("", name); + name.Should().Be(""); } - [Fact] + [TestMethod] public void ConventionBasedMethodNameFactory_ExecuteMethod_Authorize() { var container = new RestierContainerBuilder(typeof(TestApi)); @@ -92,11 +96,11 @@ public void ConventionBasedMethodNameFactory_ExecuteMethod_Authorize() var context = new OperationContext((string test) => { return null; }, "TestMethod", null, true, null, provider); var name = ConventionBasedMethodNameFactory.GetFunctionMethodName(context, RestierPipelineState.Authorization, RestierOperationMethod.Execute); - Assert.Equal("CanExecuteTestMethod", name); + name.Should().Be("CanExecuteTestMethod"); } - [Fact] + [TestMethod] public void ConventionBasedMethodNameFactory_ExecuteMethod_PreSubmit() { var container = new RestierContainerBuilder(typeof(TestApi)); @@ -104,10 +108,10 @@ public void ConventionBasedMethodNameFactory_ExecuteMethod_PreSubmit() var context = new OperationContext((string test) => { return null; }, "TestMethod", null, true, null, provider); var name = ConventionBasedMethodNameFactory.GetFunctionMethodName(context, RestierPipelineState.PreSubmit, RestierOperationMethod.Execute); - Assert.Equal("OnExecutingTestMethod", name); + name.Should().Be("OnExecutingTestMethod"); } - [Fact] + [TestMethod] public void ConventionBasedMethodNameFactory_ExecuteMethod_Submit() { var container = new RestierContainerBuilder(typeof(TestApi)); @@ -115,10 +119,10 @@ public void ConventionBasedMethodNameFactory_ExecuteMethod_Submit() var context = new OperationContext((string test) => { return null; }, "TestMethod", null, true, null, provider); var name = ConventionBasedMethodNameFactory.GetFunctionMethodName(context, RestierPipelineState.Submit, RestierOperationMethod.Execute); - Assert.Equal("", name); + name.Should().Be(""); } - [Fact] + [TestMethod] public void ConventionBasedMethodNameFactory_ExecuteMethod_PostSubmit() { var container = new RestierContainerBuilder(typeof(TestApi)); @@ -126,7 +130,7 @@ public void ConventionBasedMethodNameFactory_ExecuteMethod_PostSubmit() var context = new OperationContext((string test) => { return null; }, "TestMethod", null, true, null, provider); var name = ConventionBasedMethodNameFactory.GetFunctionMethodName(context, RestierPipelineState.PostSubmit, RestierOperationMethod.Execute); - Assert.Equal("OnExecutedTestMethod", name); + name.Should().Be("OnExecutedTestMethod"); } diff --git a/src/Microsoft.Restier.Tests.Core/DataSourceStubsTests.cs b/src/Microsoft.Restier.Tests.Core/DataSourceStubsTests.cs index cf82e893..c7429634 100644 --- a/src/Microsoft.Restier.Tests.Core/DataSourceStubsTests.cs +++ b/src/Microsoft.Restier.Tests.Core/DataSourceStubsTests.cs @@ -2,53 +2,61 @@ // Licensed under the MIT License. See License.txt in the project root for license information. using System; -using Xunit; +using FluentAssertions; +using Microsoft.Restier.Core; +using Microsoft.Restier.Tests.Shared; +using Microsoft.VisualStudio.TestTools.UnitTesting; -namespace Microsoft.Restier.Core.Tests +namespace Microsoft.Restier.Tests.Core { - public class DataSourceStubsTests + + [TestClass] + public class DataSourceStubsTests : RestierTestBase { - [Fact] + [TestMethod] public void SourceOfEntityContainerElementIsNotCallable() { - Assert.Throws(() => DataSourceStub.GetQueryableSource("EntitySet")); + Action invalidOperation = () => { DataSourceStub.GetQueryableSource("EntitySet"); }; + invalidOperation.Should().Throw(); } - [Fact] + [TestMethod] public void SourceOfComposableFunctionIsNotCallable() { - Assert.Throws(() => DataSourceStub.GetQueryableSource("Namespace", "Function")); + Action invalidOperation = () => { DataSourceStub.GetQueryableSource("Namespace", "Function"); }; + invalidOperation.Should().Throw(); } // TODO enable these when function/action is supported. - //[Fact] + //[TestMethod] //public void ResultsOfEntityContainerElementIsNotCallable() //{ // Assert.Throws(() => DataSourceStub.Results("EntitySet")); //} - //[Fact] + //[TestMethod] //public void ResultOfEntityContainerElementIsNotCallable() //{ // Assert.Throws(() => DataSourceStub.Result("Singleton")); //} - //[Fact] + //[TestMethod] //public void ResultsOfComposableFunctionIsNotCallable() //{ // Assert.Throws(() => DataSourceStub.Results("Namespace", "Function")); //} - //[Fact] + //[TestMethod] //public void ResultOfComposableFunctionIsNotCallable() //{ // Assert.Throws(() => DataSourceStub.Result("Namespace", "Function")); //} - [Fact] + [TestMethod] public void ValueIsNotCallable() { - Assert.Throws(() => DataSourceStub.GetPropertyValue(new object(), "Property")); + Action invalidOperation = () => { DataSourceStub.GetPropertyValue(new object(), "Property"); }; + invalidOperation.Should().Throw(); } } } diff --git a/src/Microsoft.Restier.Tests.Core/InvocationContextTests.cs b/src/Microsoft.Restier.Tests.Core/InvocationContextTests.cs index 9358c91e..5315516f 100644 --- a/src/Microsoft.Restier.Tests.Core/InvocationContextTests.cs +++ b/src/Microsoft.Restier.Tests.Core/InvocationContextTests.cs @@ -2,14 +2,40 @@ // Licensed under the MIT License. See License.txt in the project root for license information. using System; +using FluentAssertions; using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.DependencyInjection.Extensions; -using Xunit; +using Microsoft.Restier.Core; +using Microsoft.Restier.Tests.Shared; +using Microsoft.VisualStudio.TestTools.UnitTesting; -namespace Microsoft.Restier.Core.Tests +namespace Microsoft.Restier.Tests.Core { - public class InvocationContextTests + + [TestClass] + public class InvocationContextTests : RestierTestBase { + + [TestMethod] + public void InvocationContext_IsConfiguredCorrectly() + { + var container = new RestierContainerBuilder(typeof(TestApi)); + var provider = container.BuildContainer(); + var api = provider.GetService(); + var context = new InvocationContext(provider); + context.GetApiService().Should().BeSameAs(api); + } + + [TestMethod] + public void InvocationContext_GetsApiServicesCorrectly() + { + var container = new RestierContainerBuilder(typeof(TestApi)); + var provider = container.BuildContainer(); + var context = new InvocationContext(provider); + context.GetApiService().Should().BeSameAs(TestApi.ApiService); + } + + #region Test Resources + private class TestApi : ApiBase { private static ApiServiceA _service; @@ -39,25 +65,6 @@ public TestApi(IServiceProvider serviceProvider) : base(serviceProvider) } } - [Fact] - public void NewInvocationContextIsConfiguredCorrectly() - { - var container = new RestierContainerBuilder(typeof(TestApi)); - var provider = container.BuildContainer(); - var api = provider.GetService(); - var context = new InvocationContext(provider); - Assert.Same(api, context.GetApiService()); - } - - [Fact] - public void InvocationContextGetsApiServicesCorrectly() - { - var container = new RestierContainerBuilder(typeof(TestApi)); - var provider = container.BuildContainer(); - var context = new InvocationContext(provider); - Assert.Same(TestApi.ApiService, context.GetApiService()); - } - private interface IServiceA { } @@ -65,5 +72,8 @@ private interface IServiceA private class ApiServiceA : IServiceA { } + + #endregion + } } diff --git a/src/Microsoft.Restier.Tests.Core/Microsoft.Restier.Tests.Core.csproj b/src/Microsoft.Restier.Tests.Core/Microsoft.Restier.Tests.Core.csproj index a357fcf7..8b52582e 100644 --- a/src/Microsoft.Restier.Tests.Core/Microsoft.Restier.Tests.Core.csproj +++ b/src/Microsoft.Restier.Tests.Core/Microsoft.Restier.Tests.Core.csproj @@ -5,16 +5,17 @@ + - - - all - runtime; build; native; contentfiles; analyzers - + + + + + diff --git a/src/Microsoft.Restier.Tests.Core/Model/DefaultModelHandler.Tests.cs b/src/Microsoft.Restier.Tests.Core/Model/DefaultModelHandlerTests.cs similarity index 77% rename from src/Microsoft.Restier.Tests.Core/Model/DefaultModelHandler.Tests.cs rename to src/Microsoft.Restier.Tests.Core/Model/DefaultModelHandlerTests.cs index 6e3303fa..ca79b25d 100644 --- a/src/Microsoft.Restier.Tests.Core/Model/DefaultModelHandler.Tests.cs +++ b/src/Microsoft.Restier.Tests.Core/Model/DefaultModelHandlerTests.cs @@ -5,15 +5,80 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; +using CloudNimble.Breakdance.Restier; +using FluentAssertions; using Microsoft.Extensions.DependencyInjection; using Microsoft.OData.Edm; +using Microsoft.Restier.Core; using Microsoft.Restier.Core.Model; -using Xunit; +using Microsoft.Restier.Tests.Shared; +using Microsoft.VisualStudio.TestTools.UnitTesting; -namespace Microsoft.Restier.Core.Tests.Model +namespace Microsoft.Restier.Tests.Core.Model { - public class DefaultModelHandlerTests + + [TestClass] + public class DefaultModelHandlerTests : RestierTestBase { + + [TestMethod] + public async Task GetModelUsingDefaultModelHandler() + { + var model = await RestierTestHelpers.GetTestableModelAsync(); + model.SchemaElements.Should().HaveCount(4); + model.SchemaElements.SingleOrDefault(e => e.Name == "TestName").Should().NotBeNull(); + model.SchemaElements.SingleOrDefault(e => e.Name == "TestName2").Should().NotBeNull(); + model.SchemaElements.SingleOrDefault(e => e.Name == "TestName3").Should().NotBeNull(); + model.EntityContainer.Should().NotBeNull(); + model.EntityContainer.Elements.SingleOrDefault(e => e.Name == "TestEntitySet").Should().NotBeNull(); + model.EntityContainer.Elements.SingleOrDefault(e => e.Name == "TestEntitySet2").Should().NotBeNull(); + model.EntityContainer.Elements.SingleOrDefault(e => e.Name == "TestEntitySet3").Should().NotBeNull(); + } + + [TestMethod] + public async Task ModelBuilderShouldBeCalledOnlyOnceIfSucceeded() + { + using (var wait = new ManualResetEventSlim(false)) + { + for (var i = 0; i < 2; i++) + { + var container = new RestierContainerBuilder(typeof(TestApiB)); + var provider = container.BuildContainer(); + var tasks = PrepareThreads(50, provider, wait); + wait.Set(); + + var models = await Task.WhenAll(tasks); + models.All(e => object.ReferenceEquals(e, models[42])).Should().BeTrue(); + } + } + } + + [TestMethod] + public async Task GetModelAsyncRetriableAfterFailure() + { + using (var wait = new ManualResetEventSlim(false)) + { + var container = new RestierContainerBuilder(typeof(TestApiC)); + var provider = container.BuildContainer(); + + var tasks = PrepareThreads(6, provider, wait); + wait.Set(); + + await Task.WhenAll(tasks).ContinueWith(t => + { + t.IsFaulted.Should().BeTrue(); + tasks.All(e => e.IsFaulted).Should().BeTrue() ; + }); + + tasks = PrepareThreads(150, provider, wait); + + var models = await Task.WhenAll(tasks); + models.All(e => ReferenceEquals(e, models[42])).Should().BeTrue(); + } + } + + #region Test Resources + private class TestApiA : ApiBase { public static new IServiceCollection ConfigureApi(Type apiType, IServiceCollection services) @@ -101,44 +166,18 @@ public async Task GetModelAsync(ModelContext context, CancellationTok innerModel = await InnerHandler.GetModelAsync(context, cancellationToken); } - var entityType = new EdmEntityType( - "TestNamespace", "TestName" + _index); + var entityType = new EdmEntityType("TestNamespace", "TestName" + _index); var model = innerModel as EdmModel; - Assert.NotNull(model); + model.Should().NotBeNull(); model.AddElement(entityType); - (model.EntityContainer as EdmEntityContainer) - .AddEntitySet("TestEntitySet" + _index, entityType); + (model.EntityContainer as EdmEntityContainer).AddEntitySet("TestEntitySet" + _index, entityType); return model; } } - [Fact] - public async Task GetModelUsingDefaultModelHandler() - { - var container = new RestierContainerBuilder(typeof(TestApiA)); - var provider = container.BuildContainer(); - var api = provider.GetService(); - - var model = await api.GetModelAsync(); - Assert.Equal(4, model.SchemaElements.Count()); - Assert.NotNull(model.SchemaElements - .SingleOrDefault(e => e.Name == "TestName")); - Assert.NotNull(model.SchemaElements - .SingleOrDefault(e => e.Name == "TestName2")); - Assert.NotNull(model.SchemaElements - .SingleOrDefault(e => e.Name == "TestName3")); - Assert.NotNull(model.EntityContainer); - Assert.NotNull(model.EntityContainer.Elements - .SingleOrDefault(e => e.Name == "TestEntitySet")); - Assert.NotNull(model.EntityContainer.Elements - .SingleOrDefault(e => e.Name == "TestEntitySet2")); - Assert.NotNull(model.EntityContainer.Elements - .SingleOrDefault(e => e.Name == "TestEntitySet3")); - } - private class TestSingleCallModelBuilder : IModelBuilder { public int CalledCount; @@ -163,8 +202,7 @@ private static Task[] PrepareThreads(int count, IServiceProvider prov // To make threads better aligned. wait.Wait(); - var scopedProvider = - provider.GetRequiredService().CreateScope().ServiceProvider; + var scopedProvider = provider.GetRequiredService().CreateScope().ServiceProvider; var api = scopedProvider.GetService(); try { @@ -179,28 +217,10 @@ private static Task[] PrepareThreads(int count, IServiceProvider prov tasks[inx] = source.Task; }); - Assert.True(result.IsCompleted); + result.IsCompleted.Should().BeTrue(); return tasks; } - [Fact] - public async Task ModelBuilderShouldBeCalledOnlyOnceIfSucceeded() - { - using (var wait = new ManualResetEventSlim(false)) - { - for (var i = 0; i < 2; i++) - { - var container = new RestierContainerBuilder(typeof(TestApiB)); - var provider = container.BuildContainer(); - var tasks = PrepareThreads(50, provider, wait); - wait.Set(); - - var models = await Task.WhenAll(tasks); - Assert.True(models.All(e => object.ReferenceEquals(e, models[42]))); - } - } - } - private class TestRetryModelBuilder : IModelBuilder { public int CalledCount; @@ -217,28 +237,9 @@ public async Task GetModelAsync(ModelContext context, CancellationTok } } - [Fact] - public async Task GetModelAsyncRetriableAfterFailure() - { - using (var wait = new ManualResetEventSlim(false)) - { - var container = new RestierContainerBuilder(typeof(TestApiC)); - var provider = container.BuildContainer(); - - var tasks = PrepareThreads(6, provider, wait); - wait.Set(); + #endregion - await Task.WhenAll(tasks).ContinueWith(t => - { - Assert.True(t.IsFaulted); - Assert.True(tasks.All(e => e.IsFaulted)); - }); - tasks = PrepareThreads(150, provider, wait); - var models = await Task.WhenAll(tasks); - Assert.True(models.All(e => object.ReferenceEquals(e, models[42]))); - } - } } } diff --git a/src/Microsoft.Restier.Tests.Core/Properties/AssemblyInfo.cs b/src/Microsoft.Restier.Tests.Core/Properties/AssemblyInfo.cs deleted file mode 100644 index 6416fe10..00000000 --- a/src/Microsoft.Restier.Tests.Core/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,39 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. See License.txt in the project root for license information. - -using System.Reflection; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; - -// General Information about an assembly is controlled through the following -// set of attributes. Change these attribute values to modify the information -// associated with an assembly. -//[assembly: AssemblyTitle("Microsoft.Restier.Core.Tests")] -//[assembly: AssemblyDescription("")] -//[assembly: AssemblyConfiguration("")] -//[assembly: AssemblyCompany("Microsoft Corp.")] -//[assembly: AssemblyProduct("Microsoft.Restier.Core.Tests")] -//[assembly: AssemblyCopyright("Copyright © Microsoft Corp. 2014")] -//[assembly: AssemblyTrademark("")] -//[assembly: AssemblyCulture("")] - -// Setting ComVisible to false makes the types in this assembly not visible -// to COM components. If you need to access a type in this assembly from -// COM, set the ComVisible attribute to true on that type. -[assembly: ComVisible(false)] - -// The following GUID is for the ID of the typelib if this project is exposed to COM -[assembly: Guid("a4873eb7-0720-45da-908e-48d3d06fd40b")] - -// Version information for an assembly consists of the following four values: -// -// Major Version -// Minor Version -// Build Number -// Revision -// -// You can specify all the values or you can default the Build and Revision Numbers -// by using the '*' as shown below: -// [assembly: AssemblyVersion("1.0.*")] -//[assembly: AssemblyVersion("1.0.0.0")] -//[assembly: AssemblyFileVersion("1.0.0.0")] \ No newline at end of file diff --git a/src/Microsoft.Restier.Tests.Core/PropertyBagTests.cs b/src/Microsoft.Restier.Tests.Core/PropertyBagTests.cs index fea3e3e8..905b1f13 100644 --- a/src/Microsoft.Restier.Tests.Core/PropertyBagTests.cs +++ b/src/Microsoft.Restier.Tests.Core/PropertyBagTests.cs @@ -2,49 +2,54 @@ // Licensed under the MIT License. See License.txt in the project root for license information. using System; +using System.Threading.Tasks; +using CloudNimble.Breakdance.Restier; +using FluentAssertions; using Microsoft.Extensions.DependencyInjection; -using Xunit; +using Microsoft.Restier.Core; +using Microsoft.Restier.Tests.Shared; +using Microsoft.VisualStudio.TestTools.UnitTesting; -namespace Microsoft.Restier.Core.Tests +namespace Microsoft.Restier.Tests.Core { - public class PropertyBagTests + + [TestClass] + public class PropertyBagTests : RestierTestBase { - [Fact] - public void PropertyBagManipulatesPropertiesCorrectly() + [TestMethod] + public void PropertyBag_ManipulatesPropertiesCorrectly() { var container = new RestierContainerBuilder(typeof(TestApi)); var provider = container.BuildContainer(); var api = provider.GetService(); - Assert.False(api.HasProperty("Test")); - Assert.Null(api.GetProperty("Test")); - Assert.Null(api.GetProperty("Test")); - Assert.Equal(default(int), api.GetProperty("Test")); + api.HasProperty("Test").Should().BeFalse(); + api.GetProperty("Test").Should().BeNull(); + api.GetProperty("Test").Should().BeNull(); + api.GetProperty("Test").Should().Be(default); api.SetProperty("Test", "Test"); - Assert.True(api.HasProperty("Test")); - Assert.Equal("Test", api.GetProperty("Test")); - Assert.Equal("Test", api.GetProperty("Test")); + api.HasProperty("Test").Should().BeTrue(); + api.GetProperty("Test").Should().Be("Test"); + api.GetProperty("Test").Should().Be("Test"); api.RemoveProperty("Test"); - Assert.False(api.HasProperty("Test")); - Assert.Null(api.GetProperty("Test")); - Assert.Null(api.GetProperty("Test")); - Assert.Equal(default(int), api.GetProperty("Test")); + api.HasProperty("Test").Should().BeFalse(); + api.GetProperty("Test").Should().BeNull(); + api.GetProperty("Test").Should().BeNull(); + api.GetProperty("Test").Should().Be(default); } - [Fact] - public void DifferentPropertyBagsDoNotConflict() + [TestMethod] + public async Task PropertyBag_InstancesDoNotConflict() { - var container = new RestierContainerBuilder(typeof(TestApi)); - var provider = container.BuildContainer(); - var api = provider.GetService(); + var api = await RestierTestHelpers.GetTestableApiInstance(); api.SetProperty("Test", 2); - Assert.Equal(2, api.GetProperty("Test")); + api.GetProperty("Test").Should().Be(2); } - [Fact] + [TestMethod] public void PropertyBagsAreDisposedCorrectly() { var container = new RestierContainerBuilder(typeof(TestApi)); @@ -53,18 +58,18 @@ public void PropertyBagsAreDisposedCorrectly() var scopedProvider = scope.ServiceProvider; var api = scopedProvider.GetService(); - Assert.NotNull(api.GetApiService()); - Assert.Equal(1, MyPropertyBag.InstanceCount); + api.GetApiService().Should().NotBeNull(); + MyPropertyBag.InstanceCount.Should().Be(1); var scopedProvider2 = provider.GetRequiredService().CreateScope().ServiceProvider; var api2 = scopedProvider2.GetService(); - Assert.NotNull(api2.GetApiService()); - Assert.Equal(2, MyPropertyBag.InstanceCount); + api2.GetApiService().Should().NotBeNull(); + MyPropertyBag.InstanceCount.Should().Be(2); scope.Dispose(); - Assert.Equal(1, MyPropertyBag.InstanceCount); + MyPropertyBag.InstanceCount.Should().Be(1); } /// diff --git a/src/Microsoft.Restier.Tests.Core/ServiceConfigurationTests.cs b/src/Microsoft.Restier.Tests.Core/ServiceConfigurationTests.cs index c1bbe5f6..b8bfc595 100644 --- a/src/Microsoft.Restier.Tests.Core/ServiceConfigurationTests.cs +++ b/src/Microsoft.Restier.Tests.Core/ServiceConfigurationTests.cs @@ -2,14 +2,157 @@ // Licensed under the MIT License. See License.txt in the project root for license information. using System; +using System.Threading.Tasks; +using CloudNimble.Breakdance.Restier; +using FluentAssertions; using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.DependencyInjection.Extensions; -using Xunit; +using Microsoft.Restier.Core; +using Microsoft.Restier.Tests.Shared; +using Microsoft.VisualStudio.TestTools.UnitTesting; -namespace Microsoft.Restier.Core.Tests +namespace Microsoft.Restier.Tests.Core { - public class ServiceConfigurationTests + + [TestClass] + public class ServiceConfigurationTests : RestierTestBase { + + [TestMethod] + public async Task ContributorsAreCalledCorrectly() + { + var api = await RestierTestHelpers.GetTestableApiInstance(); + var value = api.GetApiService().Call(); + value.Should().Be("03210"); + } + + [TestMethod] + public async Task NextInjectedViaProperty() + { + var api = await RestierTestHelpers.GetTestableApiInstance(); + var value = api.GetApiService().Call(); + value.Should().Be("01"); + } + + [TestMethod] + public async Task ContextApiScopeWorksCorrectly() + { + var api = await RestierTestHelpers.GetTestableApiInstance(); + var service1 = api.GetApiService(); + + var api2 = await RestierTestHelpers.GetTestableApiInstance(); + var service2 = api2.GetApiService(); + + service1.Should().NotBe(service2); + + var api3 = await RestierTestHelpers.GetTestableApiInstance(); + var service3 = api3.GetApiService(); + + service3.Should().NotBe(service2); + } + + //RWM: I don't think this actually tests anything of value. + [TestMethod] + public async Task NothingInjectedStillWorks() + { + // Outmost service does not call inner service + var api = await RestierTestHelpers.GetTestableApiInstance(); + + var value = api.GetApiService().Call(); + value.Should().Be("42"); + + // Test expression compilation. (RWM: I don't think this works the way they thought it did.) + value = api.GetApiService().Call(); + value.Should().Be("42"); + value = api.GetApiService().Call(); + value.Should().Be("42"); + } + + [TestMethod] + public void ServiceInjectedViaProperty() + { + var container = new RestierContainerBuilder(typeof(TestApiE)); + var provider = container.BuildContainer(); + var api = provider.GetService(); + + var expected = "Text42"; + var value = api.GetApiService().Call(); + value.Should().Be(expected); + + value = api.GetApiService().Call(); + value.Should().Be(expected); + + value = api.GetApiService().Call(); + value.Should().Be(expected); + + api.GetApiService().Should().NotBe(api.GetApiService()); + } + + [TestMethod] + public void DefaultValueInConstructorUsedIfNoService() + { + var container = new RestierContainerBuilder(typeof(TestApiF)); + var provider = container.BuildContainer(); + var api = provider.GetService(); + + var value = api.GetApiService().Call(); + value.Should().Be("42"); + + value = api.GetApiService().Call(); + value.Should().Be("42"); + value = api.GetApiService().Call(); + value.Should().Be("42"); + } + + + [TestMethod] + public void MultiInjectionViaConstructor() + { + var container = new RestierContainerBuilder(typeof(TestApiG)); + var provider = container.BuildContainer(); + var api = provider.GetService(); + + var value = api.GetApiService().Call(); + value.Should().Be("0122"); + + // Test expression compilation + value = api.GetApiService().Call(); + value.Should().Be("0122"); + value = api.GetApiService().Call(); + value.Should().Be("0122"); + } + + [TestMethod] + public void NextInjectedWithInheritedField() + { + var container = new RestierContainerBuilder(typeof(TestApiI)); + var provider = container.BuildContainer(); + var api = provider.GetService(); + + var value = api.GetApiService().Call(); + value.Should().Be("4200"); + + // Test expression compilation + value = api.GetApiService().Call(); + value.Should().Be("4200"); + value = api.GetApiService().Call(); + value.Should().Be("4200"); + } + + #region Exceptions + + [TestMethod] + public async Task ThrowOnNoServiceFound() + { + var api = await RestierTestHelpers.GetTestableApiInstance(); + + Action exceptionTest = () => { api.GetApiService(); }; + exceptionTest.Should().Throw(); + } + + #endregion + + #region Test Resources + private class TestApiA : ApiBase { public static new IServiceCollection ConfigureApi(Type apiType, IServiceCollection services) @@ -334,154 +477,9 @@ public string Call() } } - [Fact] - public void ContributorsAreCalledCorrectly() - { - var container = new RestierContainerBuilder(typeof(TestApiA)); - var provider = container.BuildContainer(); - var api = provider.GetService(); - var value = api.GetApiService().Call(); - Assert.Equal("03210", value); - } - - [Fact] - public void NextInjectedViaProperty() - { - var container = new RestierContainerBuilder(typeof(TestApiB)); - var provider = container.BuildContainer(); - var api = provider.GetService(); - var value = api.GetApiService().Call(); - Assert.Equal("01", value); - - // Test expression compilation. - value = api.GetApiService().Call(); - Assert.Equal("01", value); - } - - [Fact] - public void ContextApiScopeWorksCorrectly() - { - var container = new RestierContainerBuilder(typeof(TestApiC)); - var provider = container.BuildContainer(); - var api = provider.GetService(); - - var service1 = api.GetApiService(); - - container = new RestierContainerBuilder(typeof(TestApiC)); - provider = container.BuildContainer(); - var api2 = provider.GetService(); - - var service2 = api2.GetApiService(); - - Assert.NotEqual(service1, service2); - - container = new RestierContainerBuilder(typeof(TestApiC)); - provider = container.BuildContainer(); - var api3 = provider.GetService(); - var service3 = api3.GetApiService(); + #endregion - Assert.NotEqual(service3, service2); - } - [Fact] - public void NothingInjectedStillWorks() - { - // Outmost service does not call inner service - var container = new RestierContainerBuilder(typeof(TestApiD)); - var provider = container.BuildContainer(); - var api = provider.GetService(); - - var value = api.GetApiService().Call(); - Assert.Equal("42", value); - - // Test expression compilation. - value = api.GetApiService().Call(); - Assert.Equal("42", value); - value = api.GetApiService().Call(); - Assert.Equal("42", value); - } - - [Fact] - public void ServiceInjectedViaProperty() - { - var container = new RestierContainerBuilder(typeof(TestApiE)); - var provider = container.BuildContainer(); - var api = provider.GetService(); - - var expected = "Text42"; - var value = api.GetApiService().Call(); - Assert.Equal(expected, value); - - value = api.GetApiService().Call(); - Assert.Equal(expected, value); - - value = api.GetApiService().Call(); - Assert.Equal(expected, value); - - Assert.NotEqual( - api.GetApiService(), - api.GetApiService()); - } - - [Fact] - public void DefaultValueInConstructorUsedIfNoService() - { - var container = new RestierContainerBuilder(typeof(TestApiF)); - var provider = container.BuildContainer(); - var api = provider.GetService(); - - var value = api.GetApiService().Call(); - Assert.Equal("42", value); - - value = api.GetApiService().Call(); - Assert.Equal("42", value); - value = api.GetApiService().Call(); - Assert.Equal("42", value); - } - - - [Fact] - public void MultiInjectionViaConstructor() - { - var container = new RestierContainerBuilder(typeof(TestApiG)); - var provider = container.BuildContainer(); - var api = provider.GetService(); - - var value = api.GetApiService().Call(); - Assert.Equal("0122", value); - - // Test expression compilation - value = api.GetApiService().Call(); - Assert.Equal("0122", value); - value = api.GetApiService().Call(); - Assert.Equal("0122", value); - } - - [Fact] - public void ThrowOnNoServiceFound() - { - var container = new RestierContainerBuilder(typeof(TestApiH)); - var provider = container.BuildContainer(); - var api = provider.GetService(); - - Assert.Throws(() => { api.GetApiService(); }); - } - - [Fact] - public void NextInjectedWithInheritedField() - { - var container = new RestierContainerBuilder(typeof(TestApiI)); - var provider = container.BuildContainer(); - var api = provider.GetService(); - - var value = api.GetApiService().Call(); - Assert.Equal("4200", value); - - // Test expression compilation - value = api.GetApiService().Call(); - Assert.Equal("4200", value); - value = api.GetApiService().Call(); - Assert.Equal("4200", value); - } } -} + +} \ No newline at end of file diff --git a/src/Microsoft.Restier.Tests.EntityFramework/ChangeSetPreparerTests.cs b/src/Microsoft.Restier.Tests.EntityFramework/ChangeSetPreparerTests.cs index 593b856d..16150d46 100644 --- a/src/Microsoft.Restier.Tests.EntityFramework/ChangeSetPreparerTests.cs +++ b/src/Microsoft.Restier.Tests.EntityFramework/ChangeSetPreparerTests.cs @@ -5,27 +5,31 @@ using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; -using Microsoft.Extensions.DependencyInjection; +using CloudNimble.Breakdance.Restier; +using FluentAssertions; using Microsoft.Restier.Core; using Microsoft.Restier.Core.Submit; -using Microsoft.Restier.EntityFramework.Tests.Models.Library; -using Xunit; +using Microsoft.Restier.Tests.Shared; +using Microsoft.Restier.Tests.Shared.Scenarios.Library; +using Microsoft.VisualStudio.TestTools.UnitTesting; namespace Microsoft.Restier.EntityFramework.Tests { - public class ChangeSetPreparerTests + + [TestClass] + public class ChangeSetPreparerTests : RestierTestBase { - [Fact] + + [TestMethod] public async Task ComplexTypeUpdate() { // Arrange - var container = new RestierContainerBuilder(typeof(LibraryApi)); - var provider = container.BuildContainer(); - var libraryApi = provider.GetService(); + var provider = await RestierTestHelpers.GetTestableInjectionContainer(); + var api = provider.GetTestableApiInstance(); var item = new DataModificationItem( "Readers", - typeof(Person), + typeof(Employee), null, RestierEntitySetOperation.Update, new Dictionary { { "Id", new Guid("53162782-EA1B-4712-AF26-8AA1D2AC0461") } }, @@ -35,13 +39,13 @@ public async Task ComplexTypeUpdate() var sc = new SubmitContext(provider, changeSet); // Act - var changeSetPreparer = libraryApi.GetApiService(); + var changeSetPreparer = api.GetApiService(); await changeSetPreparer.InitializeAsync(sc, CancellationToken.None).ConfigureAwait(false); - var person = item.Resource as Person; + var person = item.Resource as Employee; // Assert - Assert.NotNull(person); - Assert.Equal("332", person.Addr.Zip); + person.Should().NotBeNull(); + person.Addr.Zip.Should().Be("332"); } } } diff --git a/src/Microsoft.Restier.Tests.EntityFramework/Microsoft.Restier.Tests.EntityFramework.csproj b/src/Microsoft.Restier.Tests.EntityFramework/Microsoft.Restier.Tests.EntityFramework.csproj index 7f2cca04..5a26db65 100644 --- a/src/Microsoft.Restier.Tests.EntityFramework/Microsoft.Restier.Tests.EntityFramework.csproj +++ b/src/Microsoft.Restier.Tests.EntityFramework/Microsoft.Restier.Tests.EntityFramework.csproj @@ -5,18 +5,23 @@ + + - - - all - runtime; build; native; contentfiles; analyzers - + + + + + + + + diff --git a/src/Microsoft.Restier.Tests.EntityFramework/Models/Library/Address.cs b/src/Microsoft.Restier.Tests.EntityFramework/Models/Library/Address.cs deleted file mode 100644 index e821098f..00000000 --- a/src/Microsoft.Restier.Tests.EntityFramework/Models/Library/Address.cs +++ /dev/null @@ -1,11 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. See License.txt in the project root for license information. - -namespace Microsoft.Restier.EntityFramework.Tests.Models.Library -{ - class Address - { - public string Street { get; set; } - public string Zip { get; set; } - } -} diff --git a/src/Microsoft.Restier.Tests.EntityFramework/Models/Library/LibraryApi.cs b/src/Microsoft.Restier.Tests.EntityFramework/Models/Library/LibraryApi.cs deleted file mode 100644 index da4c4f44..00000000 --- a/src/Microsoft.Restier.Tests.EntityFramework/Models/Library/LibraryApi.cs +++ /dev/null @@ -1,41 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. See License.txt in the project root for license information. - -using System; -using System.Threading; -using System.Threading.Tasks; -using Microsoft.AspNet.OData.Builder; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.OData.Edm; -using Microsoft.Restier.Core; -using Microsoft.Restier.Core.Model; - -namespace Microsoft.Restier.EntityFramework.Tests.Models.Library -{ - class LibraryApi : EntityFrameworkApi - { - - public static new IServiceCollection ConfigureApi(Type apiType, IServiceCollection services) - { - EntityFrameworkApi.ConfigureApi(apiType, services); - services.AddService(); - - return services; - } - - private class ModelBuilder : IModelBuilder - { - public Task GetModelAsync(ModelContext context, CancellationToken cancellationToken) - { - var builder = new ODataConventionModelBuilder(); - builder.EntitySet("Readers"); - var model = builder.GetEdmModel(); - return Task.FromResult(model); - } - } - - public LibraryApi(IServiceProvider serviceProvider) : base(serviceProvider) - { - } - } -} diff --git a/src/Microsoft.Restier.Tests.EntityFramework/Models/Library/LibraryContext.cs b/src/Microsoft.Restier.Tests.EntityFramework/Models/Library/LibraryContext.cs deleted file mode 100644 index 136d7fa9..00000000 --- a/src/Microsoft.Restier.Tests.EntityFramework/Models/Library/LibraryContext.cs +++ /dev/null @@ -1,54 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. See License.txt in the project root for license information. - -using System; -using System.Data.Entity; -using Microsoft.OData.Edm; - -namespace Microsoft.Restier.EntityFramework.Tests.Models.Library -{ - class LibraryContext : DbContext - { - public LibraryContext() - : base("LibraryContext") - { - Database.SetInitializer(new TestInitializer()); - } - - public IDbSet Readers { get; set; } - } - - class TestInitializer : DropCreateDatabaseAlways - { - protected override void Seed(LibraryContext context) - { - context.Readers.Add(new Person - { - Addr = new Address { Street = "street1" }, - FullName = "p1", - Id = new Guid("53162782-EA1B-4712-AF26-8AA1D2AC0461"), - Universe = new Universe - { - BinaryProperty = new byte[] { 0x1, 0x2 }, - BooleanProperty = true, - ByteProperty = 0x3, - DateProperty = Date.Now, - DateTimeOffsetProperty = DateTimeOffset.Now, - DecimalProperty = Decimal.One, - DoubleProperty = 123.45, - DurationProperty = TimeSpan.FromHours(1.0), - GuidProperty = new Guid("53162782-EA1B-4712-AF26-8AA1D2AC0461"), - Int16Property = 12345, - Int32Property = 1234567, - Int64Property = 9876543210, - // SByteProperty = -1, - SingleProperty = (float)123.45, - // StreamProperty = new FileStream("temp.txt", FileMode.OpenOrCreate), - StringProperty = "Hello", - TimeOfDayProperty = TimeOfDay.Now - } - }); - context.SaveChanges(); - } - } -} diff --git a/src/Microsoft.Restier.Tests.EntityFramework/Models/Library/Person.cs b/src/Microsoft.Restier.Tests.EntityFramework/Models/Library/Person.cs deleted file mode 100644 index 9a6f9628..00000000 --- a/src/Microsoft.Restier.Tests.EntityFramework/Models/Library/Person.cs +++ /dev/null @@ -1,18 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. See License.txt in the project root for license information. - -using System; - -namespace Microsoft.Restier.EntityFramework.Tests.Models.Library -{ - class Person - { - public Guid Id { get; set; } - - public string FullName { get; set; } - - public Address Addr { get; set; } - - public Universe Universe { get; set; } - } -} diff --git a/src/Microsoft.Restier.Tests.EntityFramework/Models/Library/Universe.cs b/src/Microsoft.Restier.Tests.EntityFramework/Models/Library/Universe.cs deleted file mode 100644 index b7a1787c..00000000 --- a/src/Microsoft.Restier.Tests.EntityFramework/Models/Library/Universe.cs +++ /dev/null @@ -1,47 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. See License.txt in the project root for license information. - -using System; -using System.ComponentModel.DataAnnotations.Schema; -using Microsoft.OData.Edm; - -namespace Microsoft.Restier.EntityFramework.Tests.Models.Library -{ - [ComplexType] - class Universe - { - public byte[] BinaryProperty { get; set; } - - public bool BooleanProperty { get; set; } - - public byte ByteProperty { get; set; } - - public Date DateProperty { get; set; } - - public DateTimeOffset DateTimeOffsetProperty { get; set; } - - public decimal DecimalProperty { get; set; } - - public double DoubleProperty { get; set; } - - public TimeSpan DurationProperty { get; set; } - - public Guid GuidProperty { get; set; } - - public short Int16Property { get; set; } - - public int Int32Property { get; set; } - - public long Int64Property { get; set; } - - // public sbyte SByteProperty { get; set; } - - public float SingleProperty { get; set; } - - // public FileStream StreamProperty { get; set; } - - public string StringProperty { get; set; } - - public TimeOfDay TimeOfDayProperty { get; set; } - } -} diff --git a/src/Microsoft.Restier.Tests.EntityFramework/Properties/AssemblyInfo.cs b/src/Microsoft.Restier.Tests.EntityFramework/Properties/AssemblyInfo.cs deleted file mode 100644 index 742668da..00000000 --- a/src/Microsoft.Restier.Tests.EntityFramework/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,41 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. See License.txt in the project root for license information. - -using System.Reflection; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; - -// General Information about an assembly is controlled through the following -// set of attributes. Change these attribute values to modify the information -// associated with an assembly. -//[assembly: AssemblyTitle("Microsoft.Restier.EntityFramework.Tests")] -//[assembly: AssemblyDescription("")] -//[assembly: AssemblyConfiguration("")] -//[assembly: AssemblyCompany("")] -//[assembly: AssemblyProduct("Microsoft.Restier.EntityFramework.Tests")] -//[assembly: AssemblyCopyright("Copyright © 2015")] -//[assembly: AssemblyTrademark("")] -//[assembly: AssemblyCulture("")] - -// Setting ComVisible to false makes the types in this assembly not visible -// to COM components. If you need to access a type in this assembly from -// COM, set the ComVisible attribute to true on that type. -[assembly: ComVisible(false)] - -// The following GUID is for the ID of the typelib if this project is exposed to COM -[assembly: Guid("fc6d575a-25ee-4e66-a6b9-e5658180e1dc")] - -// Version information for an assembly consists of the following four values: -// -// Major Version -// Minor Version -// Build Number -// Revision -// -// You can specify all the values or you can default the Build and Revision Numbers -// by using the '*' as shown below: -// [assembly: AssemblyVersion("1.0.*")] -//[assembly: AssemblyVersion("1.0.0.0")] -//[assembly: AssemblyFileVersion("1.0.0.0")] - -[assembly: Xunit.CollectionBehavior(DisableTestParallelization = true)] \ No newline at end of file diff --git a/Microsoft.Restier.Tests.Shared/Microsoft.Restier.Tests.Shared.csproj b/src/Microsoft.Restier.Tests.Shared/Microsoft.Restier.Tests.Shared.csproj similarity index 60% rename from Microsoft.Restier.Tests.Shared/Microsoft.Restier.Tests.Shared.csproj rename to src/Microsoft.Restier.Tests.Shared/Microsoft.Restier.Tests.Shared.csproj index 14b13b08..b1acd020 100644 --- a/Microsoft.Restier.Tests.Shared/Microsoft.Restier.Tests.Shared.csproj +++ b/src/Microsoft.Restier.Tests.Shared/Microsoft.Restier.Tests.Shared.csproj @@ -5,7 +5,10 @@ + + + @@ -21,9 +24,9 @@ - - - + + + diff --git a/src/Microsoft.Restier.Tests.Shared/RestierTestBase.cs b/src/Microsoft.Restier.Tests.Shared/RestierTestBase.cs new file mode 100644 index 00000000..353d38d6 --- /dev/null +++ b/src/Microsoft.Restier.Tests.Shared/RestierTestBase.cs @@ -0,0 +1,19 @@ +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace Microsoft.Restier.Tests.Shared +{ + + /// + /// + /// + public class RestierTestBase + { + + /// + /// + /// + public TestContext TestContext { get; set; } + + } + +} \ No newline at end of file diff --git a/Microsoft.Restier.Tests.Shared/Scenarios/Library/Address.cs b/src/Microsoft.Restier.Tests.Shared/Scenarios/Library/Address.cs similarity index 100% rename from Microsoft.Restier.Tests.Shared/Scenarios/Library/Address.cs rename to src/Microsoft.Restier.Tests.Shared/Scenarios/Library/Address.cs diff --git a/Microsoft.Restier.Tests.Shared/Scenarios/Library/Book.cs b/src/Microsoft.Restier.Tests.Shared/Scenarios/Library/Book.cs similarity index 100% rename from Microsoft.Restier.Tests.Shared/Scenarios/Library/Book.cs rename to src/Microsoft.Restier.Tests.Shared/Scenarios/Library/Book.cs diff --git a/Microsoft.Restier.Tests.Shared/Scenarios/Library/Employee.cs b/src/Microsoft.Restier.Tests.Shared/Scenarios/Library/Employee.cs similarity index 100% rename from Microsoft.Restier.Tests.Shared/Scenarios/Library/Employee.cs rename to src/Microsoft.Restier.Tests.Shared/Scenarios/Library/Employee.cs diff --git a/src/Microsoft.Restier.Tests.Shared/Scenarios/Library/LibraryApi.cs b/src/Microsoft.Restier.Tests.Shared/Scenarios/Library/LibraryApi.cs new file mode 100644 index 00000000..fa58f87e --- /dev/null +++ b/src/Microsoft.Restier.Tests.Shared/Scenarios/Library/LibraryApi.cs @@ -0,0 +1,38 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +using System; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Restier.AspNet; +using Microsoft.Restier.AspNet.Model; +using Microsoft.Restier.EntityFramework; + +namespace Microsoft.Restier.Tests.Shared.Scenarios.Library +{ + class LibraryApi : EntityFrameworkApi + { + //// Need to register publisher services as MapRestierRoute is not called + //public static new IServiceCollection ConfigureApi(Type apiType, IServiceCollection services) + //{ + // EntityFrameworkApi.ConfigureApi(apiType, services); + // services.AddODataServices(); + // return services; + //} + + public LibraryApi(IServiceProvider serviceProvider) : base(serviceProvider) + { + } + + [Operation(HasSideEffects = false)] + public Book PublishBook(bool IsActive) + { + Console.WriteLine($"IsActive = {IsActive}"); + return new Book + { + Id = Guid.NewGuid(), + Title = "The Cat in the Hat" + }; + } + + } +} diff --git a/Microsoft.Restier.Tests.Shared/Scenarios/Library/LibraryContext.cs b/src/Microsoft.Restier.Tests.Shared/Scenarios/Library/LibraryContext.cs similarity index 100% rename from Microsoft.Restier.Tests.Shared/Scenarios/Library/LibraryContext.cs rename to src/Microsoft.Restier.Tests.Shared/Scenarios/Library/LibraryContext.cs diff --git a/Microsoft.Restier.Tests.Shared/Scenarios/Library/Publisher.cs b/src/Microsoft.Restier.Tests.Shared/Scenarios/Library/Publisher.cs similarity index 100% rename from Microsoft.Restier.Tests.Shared/Scenarios/Library/Publisher.cs rename to src/Microsoft.Restier.Tests.Shared/Scenarios/Library/Publisher.cs diff --git a/Microsoft.Restier.Tests.Shared/Scenarios/Library/Universe.cs b/src/Microsoft.Restier.Tests.Shared/Scenarios/Library/Universe.cs similarity index 100% rename from Microsoft.Restier.Tests.Shared/Scenarios/Library/Universe.cs rename to src/Microsoft.Restier.Tests.Shared/Scenarios/Library/Universe.cs diff --git a/Microsoft.Restier.Tests.Shared/Scenarios/Store/StoreApi.cs b/src/Microsoft.Restier.Tests.Shared/Scenarios/Store/StoreApi.cs similarity index 100% rename from Microsoft.Restier.Tests.Shared/Scenarios/Store/StoreApi.cs rename to src/Microsoft.Restier.Tests.Shared/Scenarios/Store/StoreApi.cs