Browse Source

Transfering pipeline tests

pull/257/head
Alex Van Dyke 8 years ago
parent
commit
1823a1cb03
  1. 16
      Squidex.sln
  2. 4
      src/Squidex/Pipeline/ApiCostsFilter.cs
  3. 2
      src/Squidex/Pipeline/AppApiFilter.cs
  4. 14
      tests/Squidex.Tests/GlobalSuppressions.cs
  5. 86
      tests/Squidex.Tests/Pipeline/ActionContextLogAppenderTests.cs
  6. 32
      tests/Squidex.Tests/Pipeline/ApiAuthorizeAttributeTests.cs
  7. 104
      tests/Squidex.Tests/Pipeline/ApiCostTests.cs
  8. 101
      tests/Squidex.Tests/Pipeline/ApiExceptionFilterAttributeTests.cs
  9. 90
      tests/Squidex.Tests/Pipeline/AppApiTests.cs
  10. 130
      tests/Squidex.Tests/Pipeline/AppPermissionAttributeTests.cs
  11. 46
      tests/Squidex.Tests/Pipeline/CommandMiddlewares/ETagCommandMiddlewareTests.cs
  12. 85
      tests/Squidex.Tests/Pipeline/CommandMiddlewares/EnrichWithActorCommandMiddlewareTests.cs
  13. 65
      tests/Squidex.Tests/Pipeline/CommandMiddlewares/EnrichWithAppIdCommandMiddlewareTests.cs
  14. 124
      tests/Squidex.Tests/Pipeline/CommandMiddlewares/EnrichWithSchemaIdCommandMiddlewareTests.cs
  15. 120
      tests/Squidex.Tests/Pipeline/Swagger/SwaggerHelperTests.cs
  16. 39
      tests/Squidex.Tests/Squidex.Tests.csproj

16
Squidex.sln

@ -1,6 +1,6 @@
Microsoft Visual Studio Solution File, Format Version 12.00 Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 15 # Visual Studio 15
VisualStudioVersion = 15.0.27130.2010 VisualStudioVersion = 15.0.27130.2036
MinimumVisualStudioVersion = 10.0.40219.1 MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Squidex", "src\Squidex\Squidex.csproj", "{61F6BBCE-A080-4400-B194-70E2F5D2096E}" Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Squidex", "src\Squidex\Squidex.csproj", "{61F6BBCE-A080-4400-B194-70E2F5D2096E}"
EndProject EndProject
@ -61,6 +61,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Squidex.Domain.Apps.Entitie
EndProject EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Migrate_01", "tools\Migrate_01\Migrate_01.csproj", "{A4823E14-C0E5-4A4D-B28F-27424C25C3C7}" Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Migrate_01", "tools\Migrate_01\Migrate_01.csproj", "{A4823E14-C0E5-4A4D-B28F-27424C25C3C7}"
EndProject EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Squidex.Tests", "tests\Squidex.Tests\Squidex.Tests.csproj", "{7E8CC864-4C6E-496F-A672-9F9AD8874835}"
EndProject
Global Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU Debug|Any CPU = Debug|Any CPU
@ -307,6 +309,18 @@ Global
{A4823E14-C0E5-4A4D-B28F-27424C25C3C7}.Release|x64.Build.0 = Release|Any CPU {A4823E14-C0E5-4A4D-B28F-27424C25C3C7}.Release|x64.Build.0 = Release|Any CPU
{A4823E14-C0E5-4A4D-B28F-27424C25C3C7}.Release|x86.ActiveCfg = Release|Any CPU {A4823E14-C0E5-4A4D-B28F-27424C25C3C7}.Release|x86.ActiveCfg = Release|Any CPU
{A4823E14-C0E5-4A4D-B28F-27424C25C3C7}.Release|x86.Build.0 = Release|Any CPU {A4823E14-C0E5-4A4D-B28F-27424C25C3C7}.Release|x86.Build.0 = Release|Any CPU
{7E8CC864-4C6E-496F-A672-9F9AD8874835}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{7E8CC864-4C6E-496F-A672-9F9AD8874835}.Debug|Any CPU.Build.0 = Debug|Any CPU
{7E8CC864-4C6E-496F-A672-9F9AD8874835}.Debug|x64.ActiveCfg = Debug|Any CPU
{7E8CC864-4C6E-496F-A672-9F9AD8874835}.Debug|x64.Build.0 = Debug|Any CPU
{7E8CC864-4C6E-496F-A672-9F9AD8874835}.Debug|x86.ActiveCfg = Debug|Any CPU
{7E8CC864-4C6E-496F-A672-9F9AD8874835}.Debug|x86.Build.0 = Debug|Any CPU
{7E8CC864-4C6E-496F-A672-9F9AD8874835}.Release|Any CPU.ActiveCfg = Release|Any CPU
{7E8CC864-4C6E-496F-A672-9F9AD8874835}.Release|Any CPU.Build.0 = Release|Any CPU
{7E8CC864-4C6E-496F-A672-9F9AD8874835}.Release|x64.ActiveCfg = Release|Any CPU
{7E8CC864-4C6E-496F-A672-9F9AD8874835}.Release|x64.Build.0 = Release|Any CPU
{7E8CC864-4C6E-496F-A672-9F9AD8874835}.Release|x86.ActiveCfg = Release|Any CPU
{7E8CC864-4C6E-496F-A672-9F9AD8874835}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection EndGlobalSection
GlobalSection(SolutionProperties) = preSolution GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE HideSolutionNode = FALSE

4
src/Squidex/Pipeline/ApiCostsFilter.cs

@ -35,6 +35,10 @@ namespace Squidex.Pipeline
{ {
return (ApiCostsAttribute)((IFilterContainer)this).FilterDefinition; return (ApiCostsAttribute)((IFilterContainer)this).FilterDefinition;
} }
set
{
((IFilterContainer)this).FilterDefinition = value;
}
} }
public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next) public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)

2
src/Squidex/Pipeline/AppApiFilter.cs

@ -17,7 +17,7 @@ namespace Squidex.Pipeline
{ {
private readonly IAppProvider appProvider; private readonly IAppProvider appProvider;
private sealed class AppFeature : IAppFeature public class AppFeature : IAppFeature
{ {
public IAppEntity App { get; } public IAppEntity App { get; }

14
tests/Squidex.Tests/GlobalSuppressions.cs

@ -0,0 +1,14 @@
// ==========================================================================
// GlobalSuppressions.cs
// CivicPlus implementation of Squidex Headless CMS
// ==========================================================================
// This file is used by Code Analysis to maintain SuppressMessage
// attributes that are applied to this project.
// Project-level suppressions either have no target or are given
// a specific target and scoped to a namespace, type, member, etc.
[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1633:File must have header", Justification = "CP Has their own headers.")]
[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1652:Enable XML documentation output", Justification = "CP Has their own headers.")]
[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("StyleCop.CSharp.OrderingRules", "SA1200:Using directives must be placed correctly", Justification = "Usings should be outside namespace")]
[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("StyleCop.CSharp.ReadabilityRules", "SA1101:Prefix local calls with this", Justification = "this. prefix isn't necessary.")]
[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("StyleCop.CSharp.ReadabilityRules", "SA1116:Split parameters must start on line after declaration", Justification = "Not necessary")]

86
tests/Squidex.Tests/Pipeline/ActionContextLogAppenderTests.cs

@ -0,0 +1,86 @@
// ==========================================================================
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex UG (haftungsbeschränkt)
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System;
using System.Collections.Generic;
using FakeItEasy;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Abstractions;
using Microsoft.AspNetCore.Mvc.Infrastructure;
using Microsoft.AspNetCore.Routing;
using Moq;
using Squidex.Infrastructure.Log;
using Squidex.Pipeline;
using Xunit;
namespace Squidex.Tests.Pipeline
{
public class ActionContextLogAppenderTests
{
private readonly Mock<IActionContextAccessor> actionContextAccessor = new Mock<IActionContextAccessor>();
private readonly Mock<HttpContext> httpContextMock = new Mock<HttpContext>();
private readonly Mock<ActionDescriptor> actionDescriptor = new Mock<ActionDescriptor>();
private readonly RouteData routeData = new RouteData();
private readonly Guid requestId = Guid.NewGuid();
private readonly IDictionary<object, object> items = new Dictionary<object, object>();
private readonly IObjectWriter writer = A.Fake<IObjectWriter>();
private readonly HttpRequest request = A.Fake<HttpRequest>();
private ActionContextLogAppender sut;
private ActionContext actionContext;
[Fact]
public void Append_should_get_requestId()
{
items.Add(nameof(requestId), requestId);
SetupTest();
A.CallTo(() => writer.WriteObject(It.IsAny<string>(), It.IsAny<Action<IObjectWriter>>())).Returns(writer);
sut.Append(writer);
Assert.NotNull(writer);
}
[Fact]
public void Append_should_put_requestId()
{
SetupTest();
sut.Append(writer);
}
[Fact]
public void Append_should_return_if_no_actionContext()
{
sut = new ActionContextLogAppender(actionContextAccessor.Object);
sut.Append(writer);
}
[Fact]
public void Append_should_return_if_no_httpContext_method()
{
A.CallTo(() => request.Method).Returns(string.Empty);
httpContextMock.Setup(x => x.Request).Returns(request);
actionContext = new ActionContext(httpContextMock.Object, routeData, actionDescriptor.Object);
actionContextAccessor.Setup(x => x.ActionContext).Returns(actionContext);
sut = new ActionContextLogAppender(actionContextAccessor.Object);
sut.Append(writer);
}
private void SetupTest()
{
A.CallTo(() => request.Method).Returns("Get");
httpContextMock.Setup(x => x.Items).Returns(items);
httpContextMock.Setup(x => x.Request).Returns(request);
actionContext = new ActionContext(httpContextMock.Object, routeData, actionDescriptor.Object);
actionContextAccessor.Setup(x => x.ActionContext).Returns(actionContext);
sut = new ActionContextLogAppender(actionContextAccessor.Object);
}
}
}

32
tests/Squidex.Tests/Pipeline/ApiAuthorizeAttributeTests.cs

@ -0,0 +1,32 @@
// ==========================================================================
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex UG (haftungsbeschränkt)
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using IdentityServer4.AccessTokenValidation;
using Squidex.Pipeline;
using Squidex.Shared.Identity;
using Xunit;
namespace Squidex.Tests.Pipeline
{
public class ApiAuthorizeAttributeTests
{
private ApiAuthorizeAttribute sut = new ApiAuthorizeAttribute();
[Fact]
public void AuthenticationSchemes_should_be_default()
{
Assert.Equal(IdentityServerAuthenticationDefaults.AuthenticationScheme, sut.AuthenticationSchemes);
}
[Fact]
public void MustBeAdmin_Test()
{
sut = new MustBeAdministratorAttribute();
Assert.Equal(SquidexRoles.Administrator, sut.Roles);
}
}
}

104
tests/Squidex.Tests/Pipeline/ApiCostTests.cs

@ -0,0 +1,104 @@
// ==========================================================================
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex UG (haftungsbeschränkt)
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using FakeItEasy;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.Features;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Abstractions;
using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.AspNetCore.Mvc.Infrastructure;
using Microsoft.AspNetCore.Routing;
using Moq;
using Squidex.Domain.Apps.Entities.Apps;
using Squidex.Domain.Apps.Entities.Apps.Services;
using Squidex.Infrastructure.UsageTracking;
using Squidex.Pipeline;
using Xunit;
using static Squidex.Pipeline.AppApiFilter;
namespace Squidex.Tests.Pipeline
{
public class ApiCostTests
{
private readonly Mock<IActionContextAccessor> actionContextAccessor = new Mock<IActionContextAccessor>();
private readonly RouteData routeData = new RouteData();
private readonly Mock<ActionDescriptor> actionDescriptor = new Mock<ActionDescriptor>();
private readonly IAppPlansProvider appPlanProvider = A.Fake<IAppPlansProvider>();
private readonly IUsageTracker usageTracker = A.Fake<IUsageTracker>();
private readonly long usage = 1;
private readonly Mock<HttpContext> httpContextMock = new Mock<HttpContext>();
private readonly IFeatureCollection features = new FeatureCollection();
private readonly IAppEntity appEntity = A.Fake<IAppEntity>();
private readonly IAppFeature appFeature = A.Fake<IAppFeature>();
private readonly IAppLimitsPlan appPlan = A.Fake<IAppLimitsPlan>();
private readonly Guid appId = Guid.NewGuid();
private ActionExecutingContext context;
private ActionExecutionDelegate next;
private ApiCostsFilter sut;
public ApiCostTests()
{
var actionContext = new ActionContext(httpContextMock.Object, routeData, actionDescriptor.Object);
actionContextAccessor.Setup(x => x.ActionContext).Returns(actionContext);
context = new ActionExecutingContext(actionContext, new List<IFilterMetadata>(), new Dictionary<string, object>(), null);
context.Filters.Add(new ServiceFilterAttribute(typeof(ApiCostsFilter)));
A.CallTo(() => appEntity.Id).Returns(appId);
A.CallTo(() => appFeature.App).Returns(appEntity);
features.Set<IAppFeature>(new AppFeature(appEntity));
httpContextMock.Setup(x => x.Features).Returns(features);
A.CallTo(() => usageTracker.GetMonthlyCalls(appId.ToString(), DateTime.Today))
.Returns(usage);
}
[Fact]
public async Task Should_return_429_status_code_if_max_calls_over_limit()
{
SetupSystem(2, 1);
next = new ActionExecutionDelegate(async () =>
{
return null;
});
await sut.OnActionExecutionAsync(context, next);
Assert.Equal(new StatusCodeResult(429).StatusCode, (context.Result as StatusCodeResult).StatusCode);
}
[Fact]
public async Task Should_call_next_if_weight_is_0()
{
SetupSystem(0, 1);
var result = 0;
next = new ActionExecutionDelegate(async () =>
{
result = 1;
return null;
});
await sut.OnActionExecutionAsync(context, next);
Assert.Equal(1, result);
}
private ApiCostsFilter SetupSystem(double weight, long maxCalls)
{
A.CallTo(() => appPlan.MaxApiCalls).Returns(maxCalls);
A.CallTo(() => appPlanProvider.GetPlanForApp(appFeature.App)).Returns(appPlan);
sut = new ApiCostsFilter(appPlanProvider, usageTracker);
sut.FilterDefinition = new ApiCostsAttribute(weight);
return sut;
}
}
}

101
tests/Squidex.Tests/Pipeline/ApiExceptionFilterAttributeTests.cs

@ -0,0 +1,101 @@
// ==========================================================================
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex UG (haftungsbeschränkt)
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System.Collections.Generic;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Abstractions;
using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.AspNetCore.Routing;
using Moq;
using Squidex.Domain.Apps.Entities.Apps;
using Squidex.Infrastructure;
using Squidex.Pipeline;
using Xunit;
namespace Squidex.Tests.Pipeline
{
public class ApiExceptionFilterAttributeTests
{
private readonly Mock<HttpContext> httpContextMock = new Mock<HttpContext>();
private readonly Mock<ActionDescriptor> actionDescriptor = new Mock<ActionDescriptor>();
private readonly RouteData routeData = new RouteData();
private readonly ApiExceptionFilterAttribute sut = new ApiExceptionFilterAttribute();
private readonly ExceptionContext context;
private ActionContext actionContext;
public ApiExceptionFilterAttributeTests()
{
actionContext = new ActionContext(httpContextMock.Object, routeData, actionDescriptor.Object);
context = new ExceptionContext(actionContext, new List<IFilterMetadata>());
}
[Fact]
public void Domain_Object_Not_Found_Exception_should_be_caught()
{
context.Exception = new DomainObjectNotFoundException("id", typeof(IAppEntity));
sut.OnException(context);
Assert.Equal(new NotFoundResult().StatusCode, (context.Result as NotFoundResult).StatusCode);
}
[Fact]
public void Domain_Object_Version_Exception_should_be_caught()
{
context.Exception = new DomainObjectVersionException("id", typeof(IAppEntity), 0, 1);
sut.OnException(context);
var exptectedResult = BuildErrorResult(412, new ErrorDto { Message = context.Exception.Message });
Assert.Equal(exptectedResult.StatusCode, (context.Result as ObjectResult).StatusCode);
Assert.StartsWith("Requested version", ((context.Result as ObjectResult).Value as ErrorDto).Message);
}
[Fact]
public void Domain_Exception_should_be_caught()
{
context.Exception = new DomainException("Domain exception caught.");
sut.OnException(context);
var exptectedResult = BuildErrorResult(400, new ErrorDto { Message = context.Exception.Message });
Assert.Equal(exptectedResult.StatusCode, (context.Result as ObjectResult).StatusCode);
Assert.Equal("Domain exception caught.", ((context.Result as ObjectResult).Value as ErrorDto).Message);
}
[Fact]
public void Domain_Forbidden_Exception_should_be_caught()
{
context.Exception = new DomainForbiddenException("Domain forbidden exception caught.");
sut.OnException(context);
var exptectedResult = BuildErrorResult(403, new ErrorDto { Message = context.Exception.Message });
Assert.Equal(exptectedResult.StatusCode, (context.Result as ObjectResult).StatusCode);
Assert.Equal("Domain forbidden exception caught.", ((context.Result as ObjectResult).Value as ErrorDto).Message);
}
[Fact]
public void Validation_Exception_should_be_caught()
{
var errors = new ValidationError("Validation error 1", new string[] { "prop1" });
context.Exception = new ValidationException("Validation exception caught.", errors);
sut.OnException(context);
var exptectedResult = BuildErrorResult(400, new ErrorDto { Message = context.Exception.Message });
Assert.Equal(exptectedResult.StatusCode, (context.Result as ObjectResult).StatusCode);
Assert.Equal("Validation exception caught: Validation error 1.", ((context.Result as ObjectResult).Value as ErrorDto).Message);
}
private ObjectResult BuildErrorResult(int code, ErrorDto error)
{
return new ObjectResult(error) { StatusCode = code };
}
}
}

90
tests/Squidex.Tests/Pipeline/AppApiTests.cs

@ -0,0 +1,90 @@
// ==========================================================================
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex UG (haftungsbeschränkt)
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System;
using System.Collections.Generic;
using System.Net;
using System.Threading.Tasks;
using FakeItEasy;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.Features;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Abstractions;
using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.AspNetCore.Mvc.Infrastructure;
using Microsoft.AspNetCore.Routing;
using Moq;
using Squidex.Domain.Apps.Entities;
using Squidex.Domain.Apps.Entities.Apps;
using Squidex.Domain.Apps.Entities.Apps.Services;
using Squidex.Infrastructure.UsageTracking;
using Squidex.Pipeline;
using Xunit;
namespace Squidex.Tests.Pipeline
{
public class AppApiTests
{
private readonly Mock<IActionContextAccessor> actionContextAccessor = new Mock<IActionContextAccessor>();
private readonly RouteData routeData = new RouteData();
private readonly Mock<ActionDescriptor> actionDescriptor = new Mock<ActionDescriptor>();
private readonly IAppPlansProvider appPlanProvider = A.Fake<IAppPlansProvider>();
private readonly IUsageTracker usageTracker = A.Fake<IUsageTracker>();
private readonly long usage = 1;
private readonly Mock<HttpContext> httpContextMock = new Mock<HttpContext>();
private readonly IFeatureCollection features = new FeatureCollection();
private readonly IAppEntity appEntity = A.Fake<IAppEntity>();
private readonly IAppFeature appFeature = A.Fake<IAppFeature>();
private readonly IAppProvider appProvider = A.Fake<IAppProvider>();
private readonly Guid appId = Guid.NewGuid();
private readonly ActionExecutingContext context;
private readonly AppApiFilter sut;
private ActionExecutionDelegate next;
public AppApiTests()
{
var actionContext = new ActionContext(httpContextMock.Object, routeData, actionDescriptor.Object);
actionContextAccessor.Setup(x => x.ActionContext).Returns(actionContext);
context = new ActionExecutingContext(actionContext, new List<IFilterMetadata>(), new Dictionary<string, object>(), null);
context.Filters.Add(new AppApiAttribute());
context.RouteData.Values.Add("app", "appName");
httpContextMock.Setup(x => x.Features).Returns(features);
A.CallTo(() => appProvider.GetAppAsync("appName")).Returns(appEntity);
sut = new AppApiFilter(appProvider);
}
[Fact]
public async Task Should_set_features_if_app_found()
{
next = new ActionExecutionDelegate(async () =>
{
return null;
});
await sut.OnActionExecutionAsync(context, next);
Assert.NotEmpty(context.HttpContext.Features);
}
[Fact]
public async Task Should_return_not_found_result_if_app_not_found()
{
next = new ActionExecutionDelegate(async () =>
{
return null;
});
A.CallTo(() => appProvider.GetAppAsync("appName")).Returns((IAppEntity)null);
await sut.OnActionExecutionAsync(context, next);
var result = context.Result as NotFoundResult;
Assert.NotNull(result);
Assert.Equal((int)HttpStatusCode.NotFound, result.StatusCode);
}
}
}

130
tests/Squidex.Tests/Pipeline/AppPermissionAttributeTests.cs

@ -0,0 +1,130 @@
// ==========================================================================
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex UG (haftungsbeschränkt)
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using System.Net;
using System.Security.Claims;
using FakeItEasy;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.Features;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Abstractions;
using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.AspNetCore.Mvc.Infrastructure;
using Microsoft.AspNetCore.Routing;
using Moq;
using Squidex.Domain.Apps.Core.Apps;
using Squidex.Domain.Apps.Entities.Apps;
using Squidex.Infrastructure.Security;
using Squidex.Pipeline;
using Xunit;
namespace Squidex.Tests.Pipeline
{
public class AppPermissionAttributeTests
{
private readonly Mock<HttpContext> httpContextMock = new Mock<HttpContext>();
private readonly Mock<ClaimsPrincipal> mockUser = new Mock<ClaimsPrincipal>();
private readonly Mock<ActionDescriptor> actionDescriptor = new Mock<ActionDescriptor>();
private readonly Mock<IActionContextAccessor> actionContextAccessor = new Mock<IActionContextAccessor>();
private readonly IAppEntity appEntity = A.Fake<IAppEntity>();
private readonly ClaimsIdentity identity = new ClaimsIdentity();
private readonly AppClient client = new AppClient("clientId", "secret", AppClientPermission.Reader);
private readonly IAppFeature appFeature = A.Fake<IAppFeature>();
private readonly IFeatureCollection features = new FeatureCollection();
private readonly RouteData routeData = new RouteData();
private readonly ActionExecutingContext context;
private ActionContext actionContext;
private Claim clientClaim;
private Claim subjectClaim;
private AppPermissionAttribute sut = new MustBeAppReaderAttribute();
public AppPermissionAttributeTests()
{
actionContext = new ActionContext(httpContextMock.Object, routeData, actionDescriptor.Object);
actionContextAccessor.Setup(x => x.ActionContext).Returns(actionContext);
clientClaim = new Claim("client_id", $"test:clientId");
subjectClaim = new Claim("sub", "user");
var clients = ImmutableDictionary.CreateBuilder<string, AppClient>();
clients.Add("clientId", client);
var contributors = ImmutableDictionary.CreateBuilder<string, AppContributorPermission>();
contributors.Add("user", AppContributorPermission.Owner);
A.CallTo(() => appFeature.App).Returns(appEntity);
A.CallTo(() => appEntity.Clients).Returns(new AppClients(clients.ToImmutable()));
A.CallTo(() => appEntity.Contributors).Returns(new AppContributors(contributors.ToImmutable()));
features.Set<IAppFeature>(appFeature);
mockUser.Setup(x => x.Identities).Returns(new List<ClaimsIdentity> { identity });
httpContextMock.Setup(x => x.Features).Returns(features);
httpContextMock.Setup(x => x.User).Returns(mockUser.Object);
context = new ActionExecutingContext(actionContext, new List<IFilterMetadata>(), new Dictionary<string, object>(), null);
context.Filters.Clear();
sut = new MustBeAppDeveloperAttribute();
}
[Fact]
public void Null_Permission_Returns_Not_Found()
{
// Arrange
sut = new MustBeAppReaderAttribute();
context.Filters.Add(sut);
mockUser.Setup(x => x.FindFirst(OpenIdClaims.Subject)).Returns((Claim)null);
clientClaim = new Claim("client_id", "test");
mockUser.Setup(x => x.FindFirst(OpenIdClaims.ClientId)).Returns(clientClaim);
// Act
sut.OnActionExecuting(context);
// Assert
var result = context.Result as NotFoundResult;
Assert.NotNull(result);
Assert.Equal((int)HttpStatusCode.NotFound, result.StatusCode);
}
[Fact]
public void Lower_Permission_Returns_Forbidden()
{
// Arrange
sut = new MustBeAppEditorAttribute();
context.Filters.Add(sut);
mockUser.Setup(x => x.FindFirst(OpenIdClaims.Subject)).Returns((Claim)null);
mockUser.Setup(x => x.FindFirst(OpenIdClaims.ClientId)).Returns(clientClaim);
// Act
sut.OnActionExecuting(context);
// Assert
var result = context.Result as StatusCodeResult;
Assert.NotNull(result);
Assert.Equal((int)HttpStatusCode.Forbidden, result.StatusCode);
}
[Fact]
public void Higher_Permission_Should_Get_All_Lesser_Permissions()
{
// Arrange
sut = new MustBeAppOwnerAttribute();
context.Filters.Add(sut);
mockUser.Setup(x => x.FindFirst(OpenIdClaims.Subject)).Returns(subjectClaim);
// Act
sut.OnActionExecuting(context);
// Assert
var result = context.HttpContext.User.Identities.First()?.Claims;
Assert.NotNull(result);
Assert.NotEmpty(result);
Assert.Equal(Enum.GetNames(typeof(AppPermission)).Length, result.Count());
}
}
}

46
tests/Squidex.Tests/Pipeline/CommandMiddlewares/ETagCommandMiddlewareTests.cs

@ -0,0 +1,46 @@
// ==========================================================================
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex UG (haftungsbeschränkt)
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System.Threading.Tasks;
using FakeItEasy;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Primitives;
using Squidex.Domain.Apps.Entities.Assets.Commands;
using Squidex.Infrastructure.Commands;
using Squidex.Pipeline.CommandMiddlewares;
using Xunit;
namespace Squidex.Tests.Pipeline.CommandMiddlewares
{
public class ETagCommandMiddlewareTests
{
private readonly IHttpContextAccessor httpContextAccessor = A.Fake<IHttpContextAccessor>();
private readonly ICommandBus commandBus = A.Fake<ICommandBus>();
private readonly IHeaderDictionary headers = new HeaderDictionary { { "If-Match", "1" } };
private readonly UpdateAsset command = new UpdateAsset();
private readonly EntitySavedResult entitySavedResult = new EntitySavedResult(1);
private readonly ETagCommandMiddleware sut;
public ETagCommandMiddlewareTests()
{
A.CallTo(() => httpContextAccessor.HttpContext.Request.Headers).Returns(headers);
sut = new ETagCommandMiddleware(httpContextAccessor);
}
[Fact]
public async Task Should_add_etag_header_and_expected_version()
{
var context = new CommandContext(command, commandBus);
context.Complete(entitySavedResult);
await sut.HandleAsync(context);
Assert.Equal(1, context.Command.ExpectedVersion);
Assert.Equal(new StringValues("1"), httpContextAccessor.HttpContext.Response.Headers["ETag"]);
}
}
}

85
tests/Squidex.Tests/Pipeline/CommandMiddlewares/EnrichWithActorCommandMiddlewareTests.cs

@ -0,0 +1,85 @@
// ==========================================================================
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex UG (haftungsbeschränkt)
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System;
using System.Security;
using System.Security.Claims;
using System.Threading.Tasks;
using FakeItEasy;
using Microsoft.AspNetCore.Http;
using Squidex.Domain.Apps.Entities.Contents.Commands;
using Squidex.Infrastructure.Commands;
using Squidex.Infrastructure.Security;
using Squidex.Pipeline.CommandMiddlewares;
using Xunit;
namespace Squidex.Tests.Pipeline.CommandMiddlewares
{
public class EnrichWithActorCommandMiddlewareTests
{
private readonly IHttpContextAccessor httpContextAccessor = A.Fake<IHttpContextAccessor>();
private readonly ICommandBus commandBus = A.Fake<ICommandBus>();
private readonly CreateContent command = new CreateContent { Actor = null };
[Fact]
public async Task HandleAsync_should_throw_security_exception()
{
var context = new CommandContext(command, commandBus);
var sut = SetupSystem(null, out string claimValue);
await Assert.ThrowsAsync<SecurityException>(() =>
{
return sut.HandleAsync(context);
});
}
[Fact]
public async Task HandleAsync_should_find_actor_from_subject()
{
var context = new CommandContext(command, commandBus);
var sut = SetupSystem("subject", out string claimValue);
await sut.HandleAsync(context);
Assert.Equal(claimValue, command.Actor.Identifier);
}
[Fact]
public async Task HandleAsync_should_find_actor_from_client()
{
var context = new CommandContext(command, commandBus);
var sut = SetupSystem("client", out string claimValue);
await sut.HandleAsync(context);
Assert.Equal(claimValue, command.Actor.Identifier);
}
private EnrichWithActorCommandMiddleware SetupSystem(string refTokenType, out string claimValue)
{
Claim actorClaim;
claimValue = Guid.NewGuid().ToString();
var user = new ClaimsPrincipal();
var claimsIdentity = new ClaimsIdentity();
switch (refTokenType)
{
case "subject":
actorClaim = new Claim(OpenIdClaims.Subject, claimValue);
claimsIdentity.AddClaim(actorClaim);
break;
case "client":
actorClaim = new Claim(OpenIdClaims.ClientId, claimValue);
claimsIdentity.AddClaim(actorClaim);
break;
}
user.AddIdentity(claimsIdentity);
A.CallTo(() => httpContextAccessor.HttpContext.User).Returns(user);
return new EnrichWithActorCommandMiddleware(httpContextAccessor);
}
}
}

65
tests/Squidex.Tests/Pipeline/CommandMiddlewares/EnrichWithAppIdCommandMiddlewareTests.cs

@ -0,0 +1,65 @@
// ==========================================================================
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex UG (haftungsbeschränkt)
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System;
using System.Threading.Tasks;
using FakeItEasy;
using Microsoft.AspNetCore.Http;
using Squidex.Domain.Apps.Entities.Apps;
using Squidex.Domain.Apps.Entities.Apps.State;
using Squidex.Domain.Apps.Entities.Contents.Commands;
using Squidex.Infrastructure;
using Squidex.Infrastructure.Commands;
using Squidex.Pipeline;
using Squidex.Pipeline.CommandMiddlewares;
using Xunit;
using static Squidex.Pipeline.AppApiFilter;
namespace Squidex.Tests.Pipeline.CommandMiddlewares
{
public class EnrichWithAppIdCommandMiddlewareTests
{
private readonly IHttpContextAccessor httpContextAccessor = A.Fake<IHttpContextAccessor>();
private readonly ICommandBus commandBus = A.Fake<ICommandBus>();
private readonly CreateContent command = new CreateContent { AppId = null };
[Fact]
public async Task HandleAsync_should_throw_exception_if_app_id_not_found()
{
var context = new CommandContext(command, commandBus);
var sut = SetupSystem(null);
await Assert.ThrowsAsync<InvalidOperationException>(() =>
{
return sut.HandleAsync(context);
});
}
[Fact]
public async Task HandleAsync_should_find_app_id_from_features()
{
var context = new CommandContext(command, commandBus);
var app = new AppState
{
Name = "app",
Id = Guid.NewGuid()
};
var sut = SetupSystem(app);
await sut.HandleAsync(context);
Assert.Equal(new NamedId<Guid>(app.Id, app.Name), command.AppId);
}
private EnrichWithAppIdCommandMiddleware SetupSystem(IAppEntity app)
{
var appFeature = app == null ? null : new AppFeature(app);
A.CallTo(() => httpContextAccessor.HttpContext.Features.Get<IAppFeature>()).Returns(appFeature);
return new EnrichWithAppIdCommandMiddleware(httpContextAccessor);
}
}
}

124
tests/Squidex.Tests/Pipeline/CommandMiddlewares/EnrichWithSchemaIdCommandMiddlewareTests.cs

@ -0,0 +1,124 @@
// ==========================================================================
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex UG (haftungsbeschränkt)
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using FakeItEasy;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Abstractions;
using Microsoft.AspNetCore.Mvc.Infrastructure;
using Microsoft.AspNetCore.Routing;
using Moq;
using Squidex.Domain.Apps.Core;
using Squidex.Domain.Apps.Core.Schemas;
using Squidex.Domain.Apps.Entities;
using Squidex.Domain.Apps.Entities.Apps.Commands;
using Squidex.Domain.Apps.Entities.Contents.Commands;
using Squidex.Domain.Apps.Entities.Schemas;
using Squidex.Domain.Apps.Entities.Schemas.State;
using Squidex.Infrastructure;
using Squidex.Infrastructure.Commands;
using Squidex.Pipeline.CommandMiddlewares;
using Xunit;
namespace Squidex.Tests.Pipeline.CommandMiddlewares
{
public class EnrichWithSchemaIdCommandMiddlewareTests
{
private readonly Mock<IActionContextAccessor> actionContextAccessor = new Mock<IActionContextAccessor>();
private readonly ICommandBus commandBus = A.Fake<ICommandBus>();
private readonly Mock<HttpContext> httpContextMock = new Mock<HttpContext>();
private readonly Mock<ActionDescriptor> actionDescriptor = new Mock<ActionDescriptor>();
private readonly IAppProvider appProvider = A.Fake<IAppProvider>();
private readonly Guid appId = Guid.NewGuid();
private readonly string appName = "app";
private readonly Guid schemaId = Guid.NewGuid();
private readonly string schemaName = "schema";
private readonly CreateContent command = new CreateContent();
private readonly RouteData routeData = new RouteData();
private ISchemaEntity schema;
[Fact]
public async Task HandleAsync_should_throw_exception_if_schema_not_found()
{
var context = new CommandContext(command, commandBus);
var sut = SetupSchemaCommand(false);
await Assert.ThrowsAsync<DomainObjectNotFoundException>(() =>
{
return sut.HandleAsync(context);
});
}
[Fact]
public async Task HandleAsync_should_find_schema_id_by_name()
{
var context = new CommandContext(command, commandBus);
SetupSchema();
var sut = SetupSchemaCommand(false);
await sut.HandleAsync(context);
Assert.Equal(new NamedId<Guid>(schemaId, schemaName), command.SchemaId);
}
[Fact]
public async Task HandleAsync_should_find_schema_id_by_id()
{
var context = new CommandContext(command, commandBus);
SetupSchema();
var sut = SetupSchemaCommand(true);
await sut.HandleAsync(context);
Assert.Equal(new NamedId<Guid>(schemaId, schemaName), command.SchemaId);
}
private void SetupSchema()
{
var schemaDef = new Schema(schemaName);
var stringValidatorProperties = new StringFieldProperties
{
Pattern = "A-Z"
};
var stringFieldWithValidator = new StringField(1, "validator", Partitioning.Invariant, stringValidatorProperties);
schemaDef = schemaDef.AddField(stringFieldWithValidator);
schema = new SchemaState
{
Name = schemaName,
Id = schemaId,
AppId = new NamedId<Guid>(appId, appName),
SchemaDef = schemaDef
};
}
private EnrichWithSchemaIdCommandMiddleware SetupSchemaCommand(bool byId)
{
command.AppId = new NamedId<Guid>(appId, appName);
if (byId)
{
routeData.Values.Add("name", schemaId.ToString());
A.CallTo(() => appProvider.GetSchemaAsync(appId, schemaId, false)).Returns(schema);
}
else
{
routeData.Values.Add("name", "schema");
A.CallTo(() => appProvider.GetSchemaAsync(appId, schemaName)).Returns(schema);
}
var actionContext = new ActionContext(httpContextMock.Object, routeData, actionDescriptor.Object);
actionContextAccessor.Setup(x => x.ActionContext).Returns(actionContext);
return new EnrichWithSchemaIdCommandMiddleware(appProvider, actionContextAccessor.Object);
}
}
}

120
tests/Squidex.Tests/Pipeline/Swagger/SwaggerHelperTests.cs

@ -0,0 +1,120 @@
// ==========================================================================
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex UG (haftungsbeschränkt)
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System.Threading.Tasks;
using FakeItEasy;
using Microsoft.AspNetCore.Http;
using NJsonSchema;
using NSwag;
using NSwag.AspNetCore;
using NSwag.SwaggerGeneration;
using Squidex.Config;
using Squidex.Infrastructure;
using Squidex.Pipeline.Swagger;
using Xunit;
namespace Squidex.Tests.Pipeline.Swagger
{
public class SwaggerHelperTests
{
private readonly IHttpContextAccessor contextAccessor = A.Fake<IHttpContextAccessor>();
private readonly string appName = "app";
private readonly string host = "kraken";
private readonly MyUrlsOptions myUrlsOptions = new MyUrlsOptions { BaseUrl = "www.test.com" };
private readonly SwaggerOperation operation = new SwaggerOperation();
[Fact]
public void Should_load_docs()
{
var doc = SwaggerHelper.LoadDocs("security");
Assert.StartsWith("Squidex", doc);
}
[Fact]
public void Should_throw_exception_when_base_url_is_empty()
{
var testUrlOptions = new MyUrlsOptions();
Assert.Throws<ConfigurationException>(() => testUrlOptions.BuildUrl("/api"));
}
[Fact]
public void Should_create_swagger_document()
{
var swaggerDoc = CreateSwaggerDocument();
Assert.NotNull(swaggerDoc.Tags);
Assert.Contains("application/json", swaggerDoc.Consumes);
Assert.Contains("application/json", swaggerDoc.Produces);
Assert.NotNull(swaggerDoc.Info.ExtensionData["x-logo"]);
Assert.Equal($"Squidex API for {appName} App", swaggerDoc.Info.Title);
Assert.Equal("/api", swaggerDoc.BasePath);
Assert.Equal(host, swaggerDoc.Host);
Assert.NotEmpty(swaggerDoc.SecurityDefinitions);
}
[Fact]
public void Should_create_OAuth_schema()
{
var oauthSchema = SwaggerHelper.CreateOAuthSchema(myUrlsOptions);
Assert.Equal(myUrlsOptions.BuildUrl($"{Constants.IdentityServerPrefix}/connect/token"), oauthSchema.TokenUrl);
Assert.Equal(SwaggerSecuritySchemeType.OAuth2, oauthSchema.Type);
Assert.Equal(SwaggerOAuth2Flow.Application, oauthSchema.Flow);
Assert.NotEmpty(oauthSchema.Scopes);
Assert.Contains(myUrlsOptions.BuildUrl($"{Constants.IdentityServerPrefix}/connect/token"),
oauthSchema.Description);
Assert.DoesNotContain("<TOKEN_URL>", oauthSchema.Description);
}
[Fact]
public async Task Should_get_error_dto_schema()
{
var swaggerDoc = CreateSwaggerDocument();
var schemaGenerator = new SwaggerJsonSchemaGenerator(new SwaggerSettings());
var schemaResolver = new SwaggerSchemaResolver(swaggerDoc, new SwaggerSettings());
var errorDto = await schemaGenerator.GetErrorDtoSchemaAsync(schemaResolver);
Assert.NotNull(errorDto);
}
[Fact]
public void Should_add_query_parameter()
{
operation.AddQueryParameter("test", JsonObjectType.String, "Test parameter");
Assert.Contains(operation.Parameters, p => p.Kind == SwaggerParameterKind.Query);
}
[Fact]
public void Should_add_path_parameter()
{
operation.AddPathParameter("test", JsonObjectType.String, "Test parameter");
Assert.Contains(operation.Parameters, p => p.Kind == SwaggerParameterKind.Path);
}
[Fact]
public void Should_add_body_parameter()
{
operation.AddBodyParameter("test", null, "Test parameter");
Assert.Contains(operation.Parameters, p => p.Kind == SwaggerParameterKind.Body);
}
[Fact]
public void Should_add_response_parameter()
{
operation.AddResponse("200", "Test is ok");
Assert.Contains(operation.Responses, r => r.Key == "200");
}
private SwaggerDocument CreateSwaggerDocument()
{
A.CallTo(() => contextAccessor.HttpContext.Request.Scheme).Returns("http");
A.CallTo(() => contextAccessor.HttpContext.Request.Host).Returns(new HostString(host));
return SwaggerHelper.CreateApiDocument(contextAccessor.HttpContext, myUrlsOptions, appName);
}
}
}

39
tests/Squidex.Tests/Squidex.Tests.csproj

@ -0,0 +1,39 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netcoreapp2.0</TargetFramework>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="FakeItEasy" Version="4.3.0" />
<PackageReference Include="IdentityServer4" Version="2.1.1" />
<PackageReference Include="IdentityServer4.AspNetIdentity" Version="2.1.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.5.0" />
<PackageReference Include="Moq" Version="4.7.145" />
<PackageReference Include="NJsonSchema" Version="9.10.19" />
<PackageReference Include="xunit" Version="2.3.1" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.3.1" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\src\Squidex.Domain.Users\Squidex.Domain.Users.csproj" />
<ProjectReference Include="..\..\src\Squidex\Squidex.csproj" />
</ItemGroup>
<ItemGroup>
<Reference Include="CivicPlusIdentityServer.SDK.NetCore">
<HintPath>..\..\..\identityserver\CivicPlusIdentityServer.SDK.NetCore\bin\Debug\netcoreapp2.0\CivicPlusIdentityServer.SDK.NetCore.dll</HintPath>
</Reference>
<Reference Include="RestSharp">
<HintPath>..\..\..\restsharp_core\RestSharp\bin\Debug\netstandard2.0\RestSharp.dll</HintPath>
</Reference>
<Reference Include="RestSharp.NetCore">
<HintPath>..\..\..\..\Desktop\RestSharp.NetCore.dll</HintPath>
</Reference>
</ItemGroup>
<ItemGroup>
<Service Include="{82a7f48d-3b50-4b1e-b82e-3ada8210c358}" />
</ItemGroup>
</Project>
Loading…
Cancel
Save