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
+
+
+
+
+