diff --git a/Dockerfile b/Dockerfile index 3895f5fcf..de1b25aee 100644 --- a/Dockerfile +++ b/Dockerfile @@ -24,7 +24,8 @@ RUN dotnet restore \ && dotnet test tests/Squidex.Infrastructure.Tests/Squidex.Infrastructure.Tests.csproj \ && dotnet test tests/Squidex.Domain.Apps.Core.Tests/Squidex.Domain.Apps.Core.Tests.csproj \ && dotnet test tests/Squidex.Domain.Apps.Entities.Tests/Squidex.Domain.Apps.Entities.Tests.csproj \ - && dotnet test tests/Squidex.Domain.Users.Tests/Squidex.Domain.Users.Tests.csproj + && dotnet test tests/Squidex.Domain.Users.Tests/Squidex.Domain.Users.Tests.csproj \ + && dotnet test tests/Squidex.Tests/Squidex.Tests.csproj # Publish RUN dotnet publish src/Squidex/Squidex.csproj --output /out/ --configuration Release diff --git a/Squidex.sln b/Squidex.sln index e4671fde7..67ffac379 100644 --- a/Squidex.sln +++ b/Squidex.sln @@ -1,6 +1,6 @@ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 15 -VisualStudioVersion = 15.0.27130.2010 +VisualStudioVersion = 15.0.27130.2036 MinimumVisualStudioVersion = 10.0.40219.1 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Squidex", "src\Squidex\Squidex.csproj", "{61F6BBCE-A080-4400-B194-70E2F5D2096E}" EndProject @@ -61,6 +61,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Squidex.Domain.Apps.Entitie EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Migrate_01", "tools\Migrate_01\Migrate_01.csproj", "{A4823E14-C0E5-4A4D-B28F-27424C25C3C7}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Squidex.Tests", "tests\Squidex.Tests\Squidex.Tests.csproj", "{7E8CC864-4C6E-496F-A672-9F9AD8874835}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution 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|x86.ActiveCfg = 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 GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/src/Squidex/Pipeline/ApiCostsFilter.cs b/src/Squidex/Pipeline/ApiCostsFilter.cs index 842915238..a6e14234c 100644 --- a/src/Squidex/Pipeline/ApiCostsFilter.cs +++ b/src/Squidex/Pipeline/ApiCostsFilter.cs @@ -35,6 +35,10 @@ namespace Squidex.Pipeline { return (ApiCostsAttribute)((IFilterContainer)this).FilterDefinition; } + set + { + ((IFilterContainer)this).FilterDefinition = value; + } } public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next) diff --git a/src/Squidex/Pipeline/AppApiFilter.cs b/src/Squidex/Pipeline/AppApiFilter.cs index 0750a7186..4c65a1f42 100644 --- a/src/Squidex/Pipeline/AppApiFilter.cs +++ b/src/Squidex/Pipeline/AppApiFilter.cs @@ -17,7 +17,7 @@ namespace Squidex.Pipeline { private readonly IAppProvider appProvider; - private sealed class AppFeature : IAppFeature + public class AppFeature : IAppFeature { public IAppEntity App { get; } diff --git a/src/Squidex/Pipeline/CommandMiddlewares/EnrichWithSchemaIdCommandMiddleware.cs b/src/Squidex/Pipeline/CommandMiddlewares/EnrichWithSchemaIdCommandMiddleware.cs index 40b660c8a..e385b6025 100644 --- a/src/Squidex/Pipeline/CommandMiddlewares/EnrichWithSchemaIdCommandMiddleware.cs +++ b/src/Squidex/Pipeline/CommandMiddlewares/EnrichWithSchemaIdCommandMiddleware.cs @@ -32,6 +32,8 @@ namespace Squidex.Pipeline.CommandMiddlewares if (actionContextAccessor.ActionContext == null) { await next(); + + return; } if (context.Command is ISchemaCommand schemaCommand && schemaCommand.SchemaId == null) diff --git a/src/Squidex/Pipeline/EnforceHttpsMiddleware.cs b/src/Squidex/Pipeline/EnforceHttpsMiddleware.cs index 556f90fb8..5cbc5efbe 100644 --- a/src/Squidex/Pipeline/EnforceHttpsMiddleware.cs +++ b/src/Squidex/Pipeline/EnforceHttpsMiddleware.cs @@ -36,9 +36,9 @@ namespace Squidex.Pipeline if (!string.Equals(context.Request.Scheme, "https", StringComparison.OrdinalIgnoreCase)) { - var newUrl = string.Concat("https://", hostName, context.Request.Path); + var newUrl = string.Concat("https://", hostName, context.Request.Path, context.Request.QueryString); - context.Response.Redirect(newUrl + context.Request.QueryString, true); + context.Response.Redirect(newUrl, true); } else { diff --git a/src/Squidex/Squidex.csproj b/src/Squidex/Squidex.csproj index e898de24f..773334ee1 100644 --- a/src/Squidex/Squidex.csproj +++ b/src/Squidex/Squidex.csproj @@ -11,6 +11,11 @@ true + + full + True + + diff --git a/tests/RunCoverage.ps1 b/tests/RunCoverage.ps1 index 6affb6d9e..3b46fc1dc 100644 --- a/tests/RunCoverage.ps1 +++ b/tests/RunCoverage.ps1 @@ -3,6 +3,7 @@ Param( [switch]$appsCore, [switch]$appsEntities, [switch]$users, + [switch]$web, [switch]$all ) @@ -64,6 +65,17 @@ if ($all -Or $users) { -oldStyle } +if ($all -Or $web) { + &"$folderHome\.nuget\packages\OpenCover\4.6.519\tools\OpenCover.Console.exe" ` + -register:user ` + -target:"C:\Program Files\dotnet\dotnet.exe" ` + -targetargs:"test $folderWorking\Squidex.Tests\Squidex.Tests.csproj" ` + -filter:"+[Squidex]Squidex.Pipeline*" ` + -skipautoprops ` + -output:"$folderWorking\$folderReports\Web.xml" ` + -oldStyle +} + &"$folderHome\.nuget\packages\ReportGenerator\3.1.1\tools\ReportGenerator.exe" ` -reports:"$folderWorking\$folderReports\*.xml" ` -targetdir:"$folderWorking\$folderReports\Output" \ No newline at end of file diff --git a/tests/Squidex.Tests/Pipeline/CommandMiddlewares/ETagCommandMiddlewareTests.cs b/tests/Squidex.Tests/Pipeline/CommandMiddlewares/ETagCommandMiddlewareTests.cs new file mode 100644 index 000000000..eb8f56bb1 --- /dev/null +++ b/tests/Squidex.Tests/Pipeline/CommandMiddlewares/ETagCommandMiddlewareTests.cs @@ -0,0 +1,73 @@ +// ========================================================================== +// 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.Contents.Commands; +using Squidex.Infrastructure.Commands; +using Xunit; + +namespace Squidex.Pipeline.CommandMiddlewares +{ + public class ETagCommandMiddlewareTests + { + private readonly IHttpContextAccessor httpContextAccessor = A.Fake(); + private readonly ICommandBus commandBus = A.Fake(); + private readonly IHeaderDictionary requestHeaders = new HeaderDictionary(); + private readonly ETagCommandMiddleware sut; + + public ETagCommandMiddlewareTests() + { + A.CallTo(() => httpContextAccessor.HttpContext.Request.Headers) + .Returns(requestHeaders); + + sut = new ETagCommandMiddleware(httpContextAccessor); + } + + [Fact] + public async Task Should_do_nothing_when_context_is_null() + { + A.CallTo(() => httpContextAccessor.HttpContext) + .Returns(null); + + var command = new CreateContent(); + var context = new CommandContext(command, commandBus); + + await sut.HandleAsync(context); + + Assert.Null(command.Actor); + } + + [Fact] + public async Task Should_add_expected_version_to_command() + { + requestHeaders["If-Match"] = "13"; + + var command = new CreateContent(); + var context = new CommandContext(command, commandBus); + + await sut.HandleAsync(context); + + Assert.Equal(13, context.Command.ExpectedVersion); + } + + [Fact] + public async Task Should_add_etag_header_to_response() + { + var command = new CreateContent(); + var context = new CommandContext(command, commandBus); + + context.Complete(new EntitySavedResult(17)); + + await sut.HandleAsync(context); + + Assert.Equal(new StringValues("17"), httpContextAccessor.HttpContext.Response.Headers["ETag"]); + } + } +} diff --git a/tests/Squidex.Tests/Pipeline/CommandMiddlewares/EnrichWithActorCommandMiddlewareTests.cs b/tests/Squidex.Tests/Pipeline/CommandMiddlewares/EnrichWithActorCommandMiddlewareTests.cs new file mode 100644 index 000000000..ce0ce1d9a --- /dev/null +++ b/tests/Squidex.Tests/Pipeline/CommandMiddlewares/EnrichWithActorCommandMiddlewareTests.cs @@ -0,0 +1,109 @@ +// ========================================================================== +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex UG (haftungsbeschränkt) +// All rights reserved. Licensed under the MIT license. +// ========================================================================== + +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; +using Squidex.Infrastructure.Commands; +using Squidex.Infrastructure.Security; +using Xunit; + +namespace Squidex.Pipeline.CommandMiddlewares +{ + public class EnrichWithActorCommandMiddlewareTests + { + private readonly IHttpContextAccessor httpContextAccessor = A.Fake(); + private readonly ICommandBus commandBus = A.Fake(); + private readonly HttpContext httpContext = new DefaultHttpContext(); + private readonly EnrichWithActorCommandMiddleware sut; + + public EnrichWithActorCommandMiddlewareTests() + { + A.CallTo(() => httpContextAccessor.HttpContext) + .Returns(httpContext); + + sut = new EnrichWithActorCommandMiddleware(httpContextAccessor); + } + + [Fact] + public async Task Should_throw_security_exception_when_no_subject_or_client_is_found() + { + var command = new CreateContent(); + var context = new CommandContext(command, commandBus); + + await Assert.ThrowsAsync(() => sut.HandleAsync(context)); + } + + [Fact] + public async Task Should_do_nothing_when_context_is_null() + { + A.CallTo(() => httpContextAccessor.HttpContext) + .Returns(null); + + var command = new CreateContent(); + var context = new CommandContext(command, commandBus); + + await sut.HandleAsync(context); + + Assert.Null(command.Actor); + } + + [Fact] + public async Task Should_assign_actor_from_subject() + { + httpContext.User = CreatePrincipal(OpenIdClaims.Subject, "me"); + + var command = new CreateContent(); + var context = new CommandContext(command, commandBus); + + await sut.HandleAsync(context); + + Assert.Equal(new RefToken("subject", "me"), command.Actor); + } + + [Fact] + public async Task Should_assign_actor_from_client() + { + httpContext.User = CreatePrincipal(OpenIdClaims.ClientId, "my-client"); + + var command = new CreateContent(); + var context = new CommandContext(command, commandBus); + + await sut.HandleAsync(context); + + Assert.Equal(new RefToken("client", "my-client"), command.Actor); + } + + [Fact] + public async Task Should_not_override_actor() + { + httpContext.User = CreatePrincipal(OpenIdClaims.ClientId, "my-client"); + + var command = new CreateContent { Actor = new RefToken("subject", "me") }; + var context = new CommandContext(command, commandBus); + + await sut.HandleAsync(context); + + Assert.Equal(new RefToken("subject", "me"), command.Actor); + } + + private static ClaimsPrincipal CreatePrincipal(string claimType, string claimValue) + { + var claimsPrincipal = new ClaimsPrincipal(); + var claimsIdentity = new ClaimsIdentity(); + + claimsIdentity.AddClaim(new Claim(claimType, claimValue)); + claimsPrincipal.AddIdentity(claimsIdentity); + + return claimsPrincipal; + } + } +} diff --git a/tests/Squidex.Tests/Pipeline/CommandMiddlewares/EnrichWithAppIdCommandMiddlewareTests.cs b/tests/Squidex.Tests/Pipeline/CommandMiddlewares/EnrichWithAppIdCommandMiddlewareTests.cs new file mode 100644 index 000000000..6c5633b24 --- /dev/null +++ b/tests/Squidex.Tests/Pipeline/CommandMiddlewares/EnrichWithAppIdCommandMiddlewareTests.cs @@ -0,0 +1,123 @@ +// ========================================================================== +// 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.Commands; +using Squidex.Domain.Apps.Entities.Contents.Commands; +using Squidex.Infrastructure; +using Squidex.Infrastructure.Commands; +using Xunit; + +namespace Squidex.Pipeline.CommandMiddlewares +{ + public class EnrichWithAppIdCommandMiddlewareTests + { + private readonly IHttpContextAccessor httpContextAccessor = A.Fake(); + private readonly ICommandBus commandBus = A.Fake(); + private readonly HttpContext httpContext = new DefaultHttpContext(); + private readonly EnrichWithAppIdCommandMiddleware sut; + + public EnrichWithAppIdCommandMiddlewareTests() + { + A.CallTo(() => httpContextAccessor.HttpContext) + .Returns(httpContext); + + sut = new EnrichWithAppIdCommandMiddleware(httpContextAccessor); + } + + [Fact] + public async Task Should_throw_exception_if_app_not_found() + { + var command = new CreateContent(); + var context = new CommandContext(command, commandBus); + + await Assert.ThrowsAsync(() => sut.HandleAsync(context)); + } + + [Fact] + public async Task Should_do_nothing_when_context_is_null() + { + A.CallTo(() => httpContextAccessor.HttpContext) + .Returns(null); + + var command = new CreateContent(); + var context = new CommandContext(command, commandBus); + + await sut.HandleAsync(context); + + Assert.Null(command.Actor); + } + + [Fact] + public async Task Should_assign_app_id_and_name_to_app_command() + { + SetupApp(out var appId, out var appName); + + var command = new CreateContent(); + var context = new CommandContext(command, commandBus); + + await sut.HandleAsync(context); + + Assert.Equal(new NamedId(appId, appName), command.AppId); + } + + [Fact] + public async Task Should_assign_app_id_to_app_self_command() + { + SetupApp(out var appId, out var appName); + + var command = new AddPattern(); + var context = new CommandContext(command, commandBus); + + await sut.HandleAsync(context); + + Assert.Equal(appId, command.AppId); + } + + [Fact] + public async Task Should_not_override_app_id() + { + SetupApp(out var appId, out var appName); + + var command = new AddPattern { AppId = Guid.NewGuid() }; + var context = new CommandContext(command, commandBus); + + await sut.HandleAsync(context); + + Assert.NotEqual(appId, command.AppId); + } + + [Fact] + public async Task Should_not_override_app_id_and_name() + { + SetupApp(out var appId, out var appName); + + var command = new CreateContent { AppId = new NamedId(Guid.NewGuid(), "other-app") }; + var context = new CommandContext(command, commandBus); + + await sut.HandleAsync(context); + + Assert.NotEqual(new NamedId(appId, appName), command.AppId); + } + + private void SetupApp(out Guid appId, out string appName) + { + appId = Guid.NewGuid(); + appName = "my-app"; + + var appEntity = A.Fake(); + A.CallTo(() => appEntity.Id).Returns(appId); + A.CallTo(() => appEntity.Name).Returns(appName); + + httpContext.Features.Set(new AppApiFilter.AppFeature(appEntity)); + } + } +} diff --git a/tests/Squidex.Tests/Pipeline/CommandMiddlewares/EnrichWithSchemaIdCommandMiddlewareTests.cs b/tests/Squidex.Tests/Pipeline/CommandMiddlewares/EnrichWithSchemaIdCommandMiddlewareTests.cs new file mode 100644 index 000000000..d00ae9453 --- /dev/null +++ b/tests/Squidex.Tests/Pipeline/CommandMiddlewares/EnrichWithSchemaIdCommandMiddlewareTests.cs @@ -0,0 +1,214 @@ +// ========================================================================== +// 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 Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.Infrastructure; +using Microsoft.AspNetCore.Routing; +using Squidex.Domain.Apps.Entities; +using Squidex.Domain.Apps.Entities.Apps; +using Squidex.Domain.Apps.Entities.Contents.Commands; +using Squidex.Domain.Apps.Entities.Schemas; +using Squidex.Domain.Apps.Entities.Schemas.Commands; +using Squidex.Infrastructure; +using Squidex.Infrastructure.Commands; +using Xunit; + +namespace Squidex.Pipeline.CommandMiddlewares +{ + public class EnrichWithSchemaIdCommandMiddlewareTests + { + private readonly IActionContextAccessor actionContextAccessor = A.Fake(); + private readonly IAppProvider appProvider = A.Fake(); + private readonly ICommandBus commandBus = A.Fake(); + private readonly HttpContext httpContext = new DefaultHttpContext(); + private readonly ActionContext actionContext = new ActionContext(); + private readonly EnrichWithSchemaIdCommandMiddleware sut; + + public EnrichWithSchemaIdCommandMiddlewareTests() + { + actionContext.RouteData = new RouteData(); + actionContext.HttpContext = httpContext; + + A.CallTo(() => actionContextAccessor.ActionContext) + .Returns(actionContext); + + sut = new EnrichWithSchemaIdCommandMiddleware(appProvider, actionContextAccessor); + } + + [Fact] + public async Task Should_throw_exception_if_app_not_found() + { + SetupApp(out var appId, out var appName); + SetupSchema(appId, out var schemaId, out var schemaName); + + A.CallTo(() => appProvider.GetSchemaAsync(appId, "other-schema")) + .Returns(Task.FromResult(null)); + + actionContext.RouteData.Values["name"] = "other-schema"; + + var command = new CreateContent(); + var context = new CommandContext(command, commandBus); + + await Assert.ThrowsAsync(() => sut.HandleAsync(context)); + } + + [Fact] + public async Task Should_do_nothing_when_context_is_null() + { + A.CallTo(() => actionContextAccessor.ActionContext) + .Returns(null); + + var command = new CreateContent(); + var context = new CommandContext(command, commandBus); + + await sut.HandleAsync(context); + + Assert.Null(command.Actor); + } + + [Fact] + public async Task Should_do_nothing_when_route_has_no_parameter() + { + SetupApp(out var appId, out var appName); + SetupSchema(appId, out var schemaId, out var schemaName); + + var command = new CreateContent(); + var context = new CommandContext(command, commandBus); + + await sut.HandleAsync(context); + + Assert.Null(command.Actor); + } + + [Fact] + public async Task Should_assign_schema_id_and_name_from_name() + { + SetupApp(out var appId, out var appName); + SetupSchema(appId, out var schemaId, out var schemaName); + + actionContext.RouteData.Values["name"] = schemaName; + + var command = new CreateContent(); + var context = new CommandContext(command, commandBus); + + await sut.HandleAsync(context); + + Assert.Equal(new NamedId(schemaId, schemaName), command.SchemaId); + } + + [Fact] + public async Task Should_assign_schema_id_and_name_from_id() + { + SetupApp(out var appId, out var appName); + SetupSchema(appId, out var schemaId, out var schemaName); + + actionContext.RouteData.Values["name"] = schemaId.ToString(); + + var command = new CreateContent(); + var context = new CommandContext(command, commandBus); + + await sut.HandleAsync(context); + + Assert.Equal(new NamedId(schemaId, schemaName), command.SchemaId); + } + + [Fact] + public async Task Should_assign_schema_id_from_id() + { + SetupApp(out var appId, out var appName); + SetupSchema(appId, out var schemaId, out var schemaName); + + actionContext.RouteData.Values["name"] = schemaId.ToString(); + + var command = new UpdateSchema(); + var context = new CommandContext(command, commandBus); + + await sut.HandleAsync(context); + + Assert.Equal(schemaId, command.SchemaId); + } + + [Fact] + public async Task Should_use_app_id_from_command() + { + var appId = new NamedId(Guid.NewGuid(), "my-app"); + + SetupSchema(appId.Id, out var schemaId, out var schemaName); + + actionContext.RouteData.Values["name"] = schemaId.ToString(); + + var command = new CreateContent { AppId = appId }; + var context = new CommandContext(command, commandBus); + + await sut.HandleAsync(context); + + Assert.Equal(new NamedId(schemaId, schemaName), command.SchemaId); + } + + [Fact] + public async Task Should_not_override_schema_id() + { + SetupApp(out var appId, out var appName); + SetupSchema(appId, out var schemaId, out var schemaName); + + var command = new CreateSchema { SchemaId = Guid.NewGuid() }; + var context = new CommandContext(command, commandBus); + + await sut.HandleAsync(context); + + Assert.NotEqual(schemaId, command.SchemaId); + } + + [Fact] + public async Task Should_not_override_schema_id_and_name() + { + SetupApp(out var appId, out var appName); + SetupSchema(appId, out var schemaId, out var schemaName); + + var command = new CreateContent { SchemaId = new NamedId(Guid.NewGuid(), "other-schema") }; + var context = new CommandContext(command, commandBus); + + await sut.HandleAsync(context); + + Assert.NotEqual(new NamedId(appId, appName), command.AppId); + } + + private void SetupSchema(Guid appId, out Guid schemaId, out string schemaName) + { + schemaId = Guid.NewGuid(); + schemaName = "my-schema"; + + var schemaEntity = A.Fake(); + A.CallTo(() => schemaEntity.Id).Returns(schemaId); + A.CallTo(() => schemaEntity.Name).Returns(schemaName); + + var temp1 = schemaName; + var temp2 = schemaId; + + A.CallTo(() => appProvider.GetSchemaAsync(appId, temp1)) + .Returns(schemaEntity); + A.CallTo(() => appProvider.GetSchemaAsync(appId, temp2, false)) + .Returns(schemaEntity); + } + + private void SetupApp(out Guid appId, out string appName) + { + appId = Guid.NewGuid(); + appName = "my-app"; + + var appEntity = A.Fake(); + A.CallTo(() => appEntity.Id).Returns(appId); + A.CallTo(() => appEntity.Name).Returns(appName); + + httpContext.Features.Set(new AppApiFilter.AppFeature(appEntity)); + } + } +} diff --git a/tests/Squidex.Tests/Pipeline/EnforceHttpsMiddlewareTests.cs b/tests/Squidex.Tests/Pipeline/EnforceHttpsMiddlewareTests.cs new file mode 100644 index 000000000..d61b1969f --- /dev/null +++ b/tests/Squidex.Tests/Pipeline/EnforceHttpsMiddlewareTests.cs @@ -0,0 +1,83 @@ +// ========================================================================== +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex UG (haftungsbeschränkt) +// All rights reserved. Licensed under the MIT license. +// ========================================================================== + +using System.Threading.Tasks; +using Microsoft.AspNetCore.Http; +using Squidex.Config; +using Squidex.Infrastructure.Tasks; +using Xunit; +using Options = Microsoft.Extensions.Options.Options; + +namespace Squidex.Pipeline +{ + public class EnforceHttpsMiddlewareTests + { + private bool isNextCalled; + private RequestDelegate next; + + public EnforceHttpsMiddlewareTests() + { + next = (context) => + { + isNextCalled = true; + + return TaskHelper.Done; + }; + } + + [Fact] + public async Task Should_make_permanent_redirect_if_redirect_is_required() + { + var httpContext = CreateHttpContext(); + + var sut = new EnforceHttpsMiddleware(next, Options.Create(new MyUrlsOptions { EnforceHTTPS = true })); + + await sut.Invoke(httpContext); + + Assert.False(isNextCalled); + Assert.Equal("https://squidex.local/path?query=1", httpContext.Response.Headers["Location"]); + } + + [Fact] + public async Task Should_not_redirect_if_already_on_https() + { + var httpContext = CreateHttpContext("https"); + + var sut = new EnforceHttpsMiddleware(next, Options.Create(new MyUrlsOptions { EnforceHTTPS = true })); + + await sut.Invoke(httpContext); + + Assert.True(isNextCalled); + Assert.Null((string)httpContext.Response.Headers["Location"]); + } + + [Fact] + public async Task Should_not_redirect_if_not_required() + { + var httpContext = CreateHttpContext("http"); + + var sut = new EnforceHttpsMiddleware(next, Options.Create(new MyUrlsOptions { EnforceHTTPS = false })); + + await sut.Invoke(httpContext); + + Assert.True(isNextCalled); + Assert.Null((string)httpContext.Response.Headers["Location"]); + } + + private static DefaultHttpContext CreateHttpContext(string scheme = "http") + { + var httpContext = new DefaultHttpContext(); + + httpContext.Request.QueryString = new QueryString("?query=1"); + httpContext.Request.Host = new HostString("squidex.local"); + httpContext.Request.Path = new PathString("/path"); + httpContext.Request.Scheme = scheme; + + return httpContext; + } + } +} diff --git a/tests/Squidex.Tests/Squidex.Tests.csproj b/tests/Squidex.Tests/Squidex.Tests.csproj new file mode 100644 index 000000000..5eb6cd98e --- /dev/null +++ b/tests/Squidex.Tests/Squidex.Tests.csproj @@ -0,0 +1,31 @@ + + + Exe + netcoreapp2.0 + Squidex + + + + + + + + + + + + + + + + + + + + + ..\..\Squidex.ruleset + + + + +