Browse Source

Fix app routing (#869)

* Fix resizer url.

* Fix app routing.

* Fix logic?

* Fix schema resolver.

* Cleanup.

* Unused classes removed.

* Fix
pull/870/head
Sebastian Stehle 4 years ago
committed by GitHub
parent
commit
ecfe98b077
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 10
      backend/src/Squidex.Web/Pipeline/AppResolver.cs
  2. 10
      backend/src/Squidex.Web/Pipeline/SchemaResolver.cs
  3. 6
      backend/tests/Squidex.Domain.Apps.Core.Tests/Operations/Scripting/JintUserTests.cs
  4. 40
      backend/tests/Squidex.Infrastructure.Tests/EventSourcing/Grains/EventConsumerGrainTests.cs
  5. 19
      backend/tests/Squidex.Web.Tests/Pipeline/AppResolverTests.cs
  6. 22
      backend/tests/Squidex.Web.Tests/Pipeline/SchemaResolverTests.cs
  7. 5
      backend/tools/TestSuite/TestSuite.ApiTests/AssetFixture.cs
  8. 1
      backend/tools/TestSuite/TestSuite.ApiTests/AssetFormatTests.cs
  9. 1
      backend/tools/TestSuite/TestSuite.ApiTests/AssetTests.cs
  10. 19
      backend/tools/TestSuite/TestSuite.ApiTests/ContentFixture.cs
  11. 1
      backend/tools/TestSuite/TestSuite.ApiTests/ContentLanguageTests.cs
  12. 47
      backend/tools/TestSuite/TestSuite.ApiTests/ContentQueryFixture.cs
  13. 7
      backend/tools/TestSuite/TestSuite.ApiTests/ContentQueryTests.cs
  14. 19
      backend/tools/TestSuite/TestSuite.ApiTests/ContentReferencesFixture.cs
  15. 1
      backend/tools/TestSuite/TestSuite.ApiTests/ContentReferencesTests.cs
  16. 1
      backend/tools/TestSuite/TestSuite.ApiTests/ContentUpdateTests.cs
  17. 1
      backend/tools/TestSuite/TestSuite.ApiTests/GraphQLTests.cs
  18. 1
      backend/tools/TestSuite/TestSuite.ApiTests/OpenApiTests.cs
  19. 30
      backend/tools/TestSuite/TestSuite.LoadTests/ReadingFixture.cs
  20. 2
      backend/tools/TestSuite/TestSuite.LoadTests/WritingFixture.cs
  21. 33
      backend/tools/TestSuite/TestSuite.Shared/ClientManagerFactory.cs
  22. 131
      backend/tools/TestSuite/TestSuite.Shared/ClientManagerWrapper.cs
  23. 21
      backend/tools/TestSuite/TestSuite.Shared/Factories.cs
  24. 2
      backend/tools/TestSuite/TestSuite.Shared/Fixtures/ClientFixture.cs
  25. 20
      backend/tools/TestSuite/TestSuite.Shared/Fixtures/ClientManagerFixture.cs
  26. 77
      backend/tools/TestSuite/TestSuite.Shared/Fixtures/ContentQueryFixture1to10.cs
  27. 32
      backend/tools/TestSuite/TestSuite.Shared/Fixtures/CreatedAppFixture.cs
  28. 25
      backend/tools/TestSuite/TestSuite.Shared/Fixtures/TestSchemaFixtureBase.cs
  29. 21
      backend/tools/TestSuite/TestSuite.Shared/Fixtures/TestSchemaWithReferencesFixtureBase.cs
  30. 3
      backend/tools/TestSuite/TestSuite.Shared/FodyWeavers.xml
  31. 37
      backend/tools/TestSuite/TestSuite.Shared/Model/TestEntity.cs
  32. 5
      backend/tools/TestSuite/TestSuite.Shared/TestSuite.Shared.csproj
  33. 47
      backend/tools/TestSuite/TestSuite.Shared/Utils/RandomHash.cs
  34. 68
      backend/tools/TestSuite/TestSuite.Shared/Utils/Run.cs
  35. 16
      backend/tools/TestSuite/TestSuite.Shared/Utils/TestHelpers.cs

10
backend/src/Squidex.Web/Pipeline/AppResolver.cs

@ -33,10 +33,16 @@ namespace Squidex.Web.Pipeline
{
var user = context.HttpContext.User;
var appName = context.RouteData.Values["app"]?.ToString();
if (context.RouteData.Values.TryGetValue("app", out var appValue))
{
var appName = appValue?.ToString();
if (!string.IsNullOrWhiteSpace(appName))
if (string.IsNullOrWhiteSpace(appName))
{
context.Result = new NotFoundResult();
return;
}
var isFrontend = user.IsInClient(DefaultClients.Frontend);
var app = await appProvider.GetAppAsync(appName, !isFrontend, context.HttpContext.RequestAborted);

10
backend/src/Squidex.Web/Pipeline/SchemaResolver.cs

@ -33,10 +33,16 @@ namespace Squidex.Web.Pipeline
if (appId != default)
{
var schemaIdOrName = context.RouteData.Values["schema"]?.ToString();
if (context.RouteData.Values.TryGetValue("schema", out var schemaValue))
{
var schemaIdOrName = schemaValue?.ToString();
if (!string.IsNullOrWhiteSpace(schemaIdOrName))
if (string.IsNullOrWhiteSpace(schemaIdOrName))
{
context.Result = new NotFoundResult();
return;
}
var schema = await GetSchemaAsync(appId, schemaIdOrName, context.HttpContext.User);
if (schema == null)

6
backend/tests/Squidex.Domain.Apps.Core.Tests/Operations/Scripting/JintUserTests.cs

@ -23,7 +23,7 @@ namespace Squidex.Domain.Apps.Core.Operations.Scripting
identity.AddClaim(new Claim(OpenIdClaims.ClientId, "1"));
AssetUser(identity, "1", true, false);
AssertUser(identity, "1", true, false);
}
[Fact]
@ -34,7 +34,7 @@ namespace Squidex.Domain.Apps.Core.Operations.Scripting
identity.AddClaim(new Claim(OpenIdClaims.Subject, "2"));
identity.AddClaim(new Claim(OpenIdClaims.Name, "user"));
AssetUser(identity, "2", false, true);
AssertUser(identity, "2", false, true);
}
[Fact]
@ -101,7 +101,7 @@ namespace Squidex.Domain.Apps.Core.Operations.Scripting
Assert.Equal(new[] { "2a", "2b" }, GetValue(identity, script2));
}
private static void AssetUser(ClaimsIdentity identity, string id, bool isClient, bool isUser)
private static void AssertUser(ClaimsIdentity identity, string id, bool isClient, bool isUser)
{
Assert.Equal(id, GetValue(identity, "user.id"));
Assert.Equal(isUser, GetValue(identity, "user.isUser"));

40
backend/tests/Squidex.Infrastructure.Tests/EventSourcing/Grains/EventConsumerGrainTests.cs

@ -121,7 +121,7 @@ namespace Squidex.Infrastructure.EventSourcing.Grains
await sut.CompleteAsync();
AssetGrainState(isStopped: true, position: initialPosition);
AssertGrainState(isStopped: true, position: initialPosition);
A.CallTo(() => eventStore.CreateSubscription(A<IEventSubscriber>._, A<string>._, A<string>._))
.MustNotHaveHappened();
@ -135,7 +135,7 @@ namespace Squidex.Infrastructure.EventSourcing.Grains
await sut.CompleteAsync();
AssetGrainState(isStopped: false, position: initialPosition);
AssertGrainState(isStopped: false, position: initialPosition);
A.CallTo(() => eventStore.CreateSubscription(A<IEventSubscriber>._, A<string>._, A<string>._))
.MustHaveHappenedOnceExactly();
@ -151,7 +151,7 @@ namespace Squidex.Infrastructure.EventSourcing.Grains
await sut.CompleteAsync();
AssetGrainState(isStopped: false, position: initialPosition);
AssertGrainState(isStopped: false, position: initialPosition);
A.CallTo(() => eventStore.CreateSubscription(A<IEventSubscriber>._, A<string>._, A<string>._))
.MustHaveHappenedOnceExactly();
@ -165,7 +165,7 @@ namespace Squidex.Infrastructure.EventSourcing.Grains
await sut.CompleteAsync();
AssetGrainState(isStopped: false, position: initialPosition);
AssertGrainState(isStopped: false, position: initialPosition);
A.CallTo(() => eventStore.CreateSubscription(A<IEventSubscriber>._, A<string>._, A<string>._))
.MustHaveHappenedOnceExactly();
@ -181,7 +181,7 @@ namespace Squidex.Infrastructure.EventSourcing.Grains
await sut.CompleteAsync();
AssetGrainState(isStopped: true, position: initialPosition);
AssertGrainState(isStopped: true, position: initialPosition);
A.CallTo(() => grainState.WriteAsync())
.MustHaveHappenedOnceExactly();
@ -200,7 +200,7 @@ namespace Squidex.Infrastructure.EventSourcing.Grains
await sut.CompleteAsync();
AssetGrainState(isStopped: false, position: null);
AssertGrainState(isStopped: false, position: null);
A.CallTo(() => grainState.WriteAsync())
.MustHaveHappened(2, Times.Exactly);
@ -228,7 +228,7 @@ namespace Squidex.Infrastructure.EventSourcing.Grains
await sut.CompleteAsync();
AssetGrainState(isStopped: false, position: storedEvent.EventPosition, count: 1);
AssertGrainState(isStopped: false, position: storedEvent.EventPosition, count: 1);
A.CallTo(() => grainState.WriteAsync())
.MustHaveHappenedOnceExactly();
@ -254,7 +254,7 @@ namespace Squidex.Infrastructure.EventSourcing.Grains
await sut.CompleteAsync();
AssetGrainState(isStopped: false, position: storedEvent.EventPosition, count: 5);
AssertGrainState(isStopped: false, position: storedEvent.EventPosition, count: 5);
A.CallTo(() => grainState.WriteAsync())
.MustHaveHappened(5, Times.Exactly);
@ -280,7 +280,7 @@ namespace Squidex.Infrastructure.EventSourcing.Grains
await sut.CompleteAsync();
AssetGrainState(isStopped: false, position: storedEvent.EventPosition, count: 5);
AssertGrainState(isStopped: false, position: storedEvent.EventPosition, count: 5);
A.CallTo(() => grainState.WriteAsync())
.MustHaveHappenedOnceExactly();
@ -302,7 +302,7 @@ namespace Squidex.Infrastructure.EventSourcing.Grains
await sut.CompleteAsync();
AssetGrainState(isStopped: false, position: storedEvent.EventPosition);
AssertGrainState(isStopped: false, position: storedEvent.EventPosition);
A.CallTo(() => grainState.WriteAsync())
.MustHaveHappenedOnceExactly();
@ -324,7 +324,7 @@ namespace Squidex.Infrastructure.EventSourcing.Grains
await sut.CompleteAsync();
AssetGrainState(isStopped: false, position: storedEvent.EventPosition);
AssertGrainState(isStopped: false, position: storedEvent.EventPosition);
A.CallTo(() => grainState.WriteAsync())
.MustHaveHappenedOnceExactly();
@ -343,7 +343,7 @@ namespace Squidex.Infrastructure.EventSourcing.Grains
await sut.CompleteAsync();
AssetGrainState(isStopped: false, position: initialPosition);
AssertGrainState(isStopped: false, position: initialPosition);
A.CallTo(() => eventConsumer.On(envelope))
.MustNotHaveHappened();
@ -361,7 +361,7 @@ namespace Squidex.Infrastructure.EventSourcing.Grains
await sut.CompleteAsync();
AssetGrainState(isStopped: true, position: initialPosition, error: ex.ToString());
AssertGrainState(isStopped: true, position: initialPosition, error: ex.ToString());
A.CallTo(() => grainState.WriteAsync())
.MustHaveHappenedOnceExactly();
@ -382,7 +382,7 @@ namespace Squidex.Infrastructure.EventSourcing.Grains
await sut.CompleteAsync();
AssetGrainState(isStopped: false, position: initialPosition);
AssertGrainState(isStopped: false, position: initialPosition);
A.CallTo(() => grainState.WriteAsync())
.MustNotHaveHappened();
@ -415,7 +415,7 @@ namespace Squidex.Infrastructure.EventSourcing.Grains
await sut.CompleteAsync();
AssetGrainState(isStopped: true, position: initialPosition, error: ex.ToString());
AssertGrainState(isStopped: true, position: initialPosition, error: ex.ToString());
A.CallTo(() => grainState.WriteAsync())
.MustHaveHappenedOnceExactly();
@ -439,7 +439,7 @@ namespace Squidex.Infrastructure.EventSourcing.Grains
await sut.CompleteAsync();
AssetGrainState(isStopped: true, position: initialPosition, error: ex.ToString());
AssertGrainState(isStopped: true, position: initialPosition, error: ex.ToString());
A.CallTo(() => eventConsumer.On(envelope))
.MustHaveHappened();
@ -466,7 +466,7 @@ namespace Squidex.Infrastructure.EventSourcing.Grains
await sut.CompleteAsync();
AssetGrainState(isStopped: true, position: initialPosition, error: ex.ToString());
AssertGrainState(isStopped: true, position: initialPosition, error: ex.ToString());
A.CallTo(() => eventConsumer.On(envelope))
.MustNotHaveHappened();
@ -497,7 +497,7 @@ namespace Squidex.Infrastructure.EventSourcing.Grains
await sut.StartAsync();
await sut.StartAsync();
AssetGrainState(isStopped: false, position: initialPosition);
AssertGrainState(isStopped: false, position: initialPosition);
A.CallTo(() => eventConsumer.On(envelope))
.MustHaveHappened();
@ -527,7 +527,7 @@ namespace Squidex.Infrastructure.EventSourcing.Grains
await sut.CompleteAsync();
AssetGrainState(isStopped: true, position: storedEvent.EventPosition, error: ex.ToString(), 1);
AssertGrainState(isStopped: true, position: storedEvent.EventPosition, error: ex.ToString(), 1);
}
private Task OnErrorAsync(IEventSubscription subscription, Exception exception)
@ -540,7 +540,7 @@ namespace Squidex.Infrastructure.EventSourcing.Grains
return sut.OnEventAsync(subscription, ev);
}
private void AssetGrainState(bool isStopped = false, string? position = null, string? error = null, int count = 0)
private void AssertGrainState(bool isStopped = false, string? position = null, string? error = null, int count = 0)
{
var expected = new EventConsumerState { IsStopped = isStopped, Position = position, Error = error, Count = count };

19
backend/tests/Squidex.Web.Tests/Pipeline/AppResolverTests.cs

@ -57,6 +57,25 @@ namespace Squidex.Web.Pipeline
sut = new AppResolver(appProvider);
}
[Theory]
[InlineData(null)]
[InlineData("")]
[InlineData(" ")]
public async Task Should_return_not_found_if_app_name_is_null(string? app)
{
SetupUser();
actionExecutingContext.RouteData.Values["app"] = app;
await sut.OnActionExecutionAsync(actionExecutingContext, next);
Assert.IsType<NotFoundResult>(actionExecutingContext.Result);
Assert.False(isNextCalled);
A.CallTo(() => appProvider.GetAppAsync(A<string>._, false, httpContext.RequestAborted))
.MustNotHaveHappened();
}
[Fact]
public async Task Should_return_not_found_if_app_not_found()
{

22
backend/tests/Squidex.Web.Tests/Pipeline/SchemaResolverTests.cs

@ -60,6 +60,22 @@ namespace Squidex.Web.Pipeline
sut = new SchemaResolver(appProvider);
}
[Theory]
[InlineData(null)]
[InlineData("")]
[InlineData(" ")]
public async Task Should_return_not_found_if_schema_name_is_null(string? schema)
{
actionContext.RouteData.Values["schema"] = schema;
await sut.OnActionExecutionAsync(actionExecutingContext, next);
AssertNotFound();
A.CallTo(() => appProvider.GetSchemaAsync(appId.Id, A<DomainId>._, true, httpContext.RequestAborted))
.MustNotHaveHappened();
}
[Fact]
public async Task Should_return_not_found_if_schema_not_published_when_attribute_applied()
{
@ -73,7 +89,7 @@ namespace Squidex.Web.Pipeline
await sut.OnActionExecutionAsync(actionExecutingContext, next);
AssetNotFound();
AssertNotFound();
}
[Fact]
@ -101,7 +117,7 @@ namespace Squidex.Web.Pipeline
await sut.OnActionExecutionAsync(actionExecutingContext, next);
AssetNotFound();
AssertNotFound();
}
[Fact]
@ -193,7 +209,7 @@ namespace Squidex.Web.Pipeline
.MustNotHaveHappened();
}
private void AssetNotFound()
private void AssertNotFound()
{
Assert.IsType<NotFoundResult>(actionExecutingContext.Result);
Assert.False(isNextCalled);

5
backend/tools/TestSuite/TestSuite.Shared/Fixtures/AssetFixture.cs → backend/tools/TestSuite/TestSuite.ApiTests/AssetFixture.cs

@ -6,13 +6,12 @@
// ==========================================================================
using Squidex.ClientLibrary.Management;
using TestSuite.Fixtures;
namespace TestSuite.Fixtures
namespace TestSuite.ApiTests
{
public class AssetFixture : CreatedAppFixture
{
public IAssetsClient Assets => Squidex.Assets;
public async Task<MemoryStream> DownloadAsync(AssetDto asset, int? version = null)
{
var temp = new MemoryStream();

1
backend/tools/TestSuite/TestSuite.ApiTests/AssetFormatTests.cs

@ -6,7 +6,6 @@
// ==========================================================================
using Squidex.ClientLibrary.Management;
using TestSuite.Fixtures;
using Xunit;
#pragma warning disable SA1300 // Element should begin with upper-case letter

1
backend/tools/TestSuite/TestSuite.ApiTests/AssetTests.cs

@ -7,7 +7,6 @@
using Squidex.Assets;
using Squidex.ClientLibrary.Management;
using TestSuite.Fixtures;
using Xunit;
#pragma warning disable SA1300 // Element should begin with upper-case letter

19
backend/tools/TestSuite/TestSuite.Shared/Utils/RandomString.cs → backend/tools/TestSuite/TestSuite.ApiTests/ContentFixture.cs

@ -5,22 +5,15 @@
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
namespace TestSuite.Utils
{
public static class RandomString
{
private static readonly Random Random = new Random();
using TestSuite.Fixtures;
public static string Create(int length)
namespace TestSuite.ApiTests
{
var chars = new char[length];
for (var i = 0; i < length; i++)
public sealed class ContentFixture : TestSchemaFixtureBase
{
public ContentFixture()
: base("my-writes")
{
chars[i] = (char)Random.Next(48, 122);
}
return new string(chars);
}
}
}

1
backend/tools/TestSuite/TestSuite.ApiTests/ContentLanguageTests.cs

@ -6,7 +6,6 @@
// ==========================================================================
using Squidex.ClientLibrary;
using TestSuite.Fixtures;
using TestSuite.Model;
using Xunit;

47
backend/tools/TestSuite/TestSuite.ApiTests/ContentQueryFixture.cs

@ -0,0 +1,47 @@
// ==========================================================================
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex UG (haftungsbeschraenkt)
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using Squidex.ClientLibrary;
using TestSuite.Fixtures;
using TestSuite.Model;
namespace TestSuite.ApiTests
{
public sealed class ContentQueryFixture : TestSchemaFixtureBase
{
public ContentQueryFixture()
: base("my-reads")
{
}
public override async Task InitializeAsync()
{
await base.InitializeAsync();
await DisposeAsync();
for (var i = 10; i > 0; i--)
{
var data = TestEntity.CreateTestEntry(i);
await Contents.CreateAsync(data, ContentCreateOptions.AsPublish);
}
}
public override async Task DisposeAsync()
{
await base.DisposeAsync();
var contents = await Contents.GetAsync();
foreach (var content in contents.Items)
{
await Contents.DeleteAsync(content);
}
}
}
}

7
backend/tools/TestSuite/TestSuite.ApiTests/ContentQueryTests.cs

@ -8,7 +8,6 @@
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Squidex.ClientLibrary;
using TestSuite.Fixtures;
using TestSuite.Model;
using Xunit;
@ -16,11 +15,11 @@ using Xunit;
namespace TestSuite.ApiTests
{
public class ContentQueryTests : IClassFixture<ContentQueryFixture1to10>
public class ContentQueryTests : IClassFixture<ContentQueryFixture>
{
public ContentQueryFixture1to10 _ { get; }
public ContentQueryFixture _ { get; }
public ContentQueryTests(ContentQueryFixture1to10 fixture)
public ContentQueryTests(ContentQueryFixture fixture)
{
_ = fixture;
}

19
backend/tools/TestSuite/TestSuite.ApiTests/ContentReferencesFixture.cs

@ -0,0 +1,19 @@
// ==========================================================================
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex UG (haftungsbeschraenkt)
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using TestSuite.Fixtures;
namespace TestSuite.ApiTests
{
public sealed class ContentReferencesFixture : TestSchemaWithReferencesFixtureBase
{
public ContentReferencesFixture()
: base("my-references")
{
}
}
}

1
backend/tools/TestSuite/TestSuite.ApiTests/ContentReferencesTests.cs

@ -6,7 +6,6 @@
// ==========================================================================
using Squidex.ClientLibrary;
using TestSuite.Fixtures;
using TestSuite.Model;
using Xunit;

1
backend/tools/TestSuite/TestSuite.ApiTests/ContentUpdateTests.cs

@ -8,7 +8,6 @@
using Newtonsoft.Json.Linq;
using Squidex.ClientLibrary;
using Squidex.ClientLibrary.Management;
using TestSuite.Fixtures;
using TestSuite.Model;
using Xunit;

1
backend/tools/TestSuite/TestSuite.ApiTests/GraphQLTests.cs

@ -8,7 +8,6 @@
using Newtonsoft.Json.Linq;
using Squidex.ClientLibrary;
using Squidex.ClientLibrary.Management;
using TestSuite.Fixtures;
using TestSuite.Model;
using Xunit;

1
backend/tools/TestSuite/TestSuite.ApiTests/OpenApiTests.cs

@ -6,7 +6,6 @@
// ==========================================================================
using NSwag;
using TestSuite.Fixtures;
using Xunit;
#pragma warning disable SA1300 // Element should begin with upper-case letter

30
backend/tools/TestSuite/TestSuite.LoadTests/ReadingFixture.cs

@ -5,15 +5,43 @@
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using Squidex.ClientLibrary;
using TestSuite.Fixtures;
using TestSuite.Model;
namespace TestSuite.LoadTests
{
public sealed class ReadingFixture : ContentQueryFixture1to10
public sealed class ReadingFixture : TestSchemaFixtureBase
{
public ReadingFixture()
: base("benchmark-reading")
{
}
public override async Task InitializeAsync()
{
await base.InitializeAsync();
await DisposeAsync();
for (var i = 10; i > 0; i--)
{
var data = TestEntity.CreateTestEntry(i);
await Contents.CreateAsync(data, ContentCreateOptions.AsPublish);
}
}
public override async Task DisposeAsync()
{
await base.DisposeAsync();
var contents = await Contents.GetAsync();
foreach (var content in contents.Items)
{
await Contents.DeleteAsync(content);
}
}
}
}

2
backend/tools/TestSuite/TestSuite.LoadTests/WritingFixture.cs

@ -9,7 +9,7 @@ using TestSuite.Fixtures;
namespace TestSuite.LoadTests
{
public sealed class WritingFixture : ContentFixture
public sealed class WritingFixture : TestSchemaFixtureBase
{
public WritingFixture()
: base("benchmark_writing")

33
backend/tools/TestSuite/TestSuite.Shared/ClientManagerFactory.cs

@ -1,33 +0,0 @@
// ==========================================================================
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex UG (haftungsbeschraenkt)
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
namespace TestSuite
{
public static class ClientManagerFactory
{
private static Task<ClientManagerWrapper> manager;
public static Task<ClientManagerWrapper> CreateAsync()
{
if (manager == null)
{
manager = CreateInternalAsync();
}
return manager;
}
private static async Task<ClientManagerWrapper> CreateInternalAsync()
{
var clientManager = new ClientManagerWrapper();
await clientManager.ConnectAsync();
return clientManager;
}
}
}

131
backend/tools/TestSuite/TestSuite.Shared/ClientManagerWrapper.cs

@ -5,7 +5,6 @@
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using Lazy;
using Microsoft.Extensions.Configuration;
using Squidex.ClientLibrary;
using Squidex.ClientLibrary.Configuration;
@ -16,40 +15,63 @@ namespace TestSuite
{
public sealed class ClientManagerWrapper
{
private static Task<ClientManagerWrapper> manager;
private readonly Lazy<IAppsClient> apps;
private readonly Lazy<IAssetsClient> assets;
private readonly Lazy<IBackupsClient> backups;
private readonly Lazy<ILanguagesClient> languages;
private readonly Lazy<IPingClient> ping;
private readonly Lazy<IRulesClient> rules;
private readonly Lazy<ISchemasClient> schemas;
private readonly Lazy<ITemplatesClient> templates;
public SquidexClientManager ClientManager { get; set; }
public SquidexClientManager ClientManager { get; }
[Lazy]
public IAppsClient Apps => ClientManager.CreateAppsClient();
public IAppsClient Apps
{
get => apps.Value;
}
[Lazy]
public IAssetsClient Assets => ClientManager.CreateAssetsClient();
public IAssetsClient Assets
{
get => assets.Value;
}
[Lazy]
public IBackupsClient Backups => ClientManager.CreateBackupsClient();
public IBackupsClient Backups
{
get => backups.Value;
}
[Lazy]
public ILanguagesClient Languages => ClientManager.CreateLanguagesClient();
public ILanguagesClient Languages
{
get => languages.Value;
}
[Lazy]
public IPingClient Ping => ClientManager.CreatePingClient();
public IPingClient Ping
{
get => ping.Value;
}
[Lazy]
public IRulesClient Rules => ClientManager.CreateRulesClient();
public IRulesClient Rules
{
get => rules.Value;
}
[Lazy]
public ISchemasClient Schemas => ClientManager.CreateSchemasClient();
public ISchemasClient Schemas
{
get => schemas.Value;
}
[Lazy]
public ITemplatesClient Templates => ClientManager.CreateTemplatesClient();
public ITemplatesClient Templates
{
get => templates.Value;
}
public ClientManagerWrapper()
{
var appName = GetValue("config:app:name", "integration-tests");
var clientId = GetValue("config:client:id", "root");
var clientSecret = GetValue("config:client:secret", "xeLd6jFxqbXJrfmNLlO2j1apagGGGSyZJhFnIuHp4I0=");
var serverUrl = GetValue("config:server:url", "https://localhost:5001");
var appName = TestHelpers.GetValue("config:app:name", "integration-tests");
var clientId = TestHelpers.GetValue("config:client:id", "root");
var clientSecret = TestHelpers.GetValue("config:client:secret", "xeLd6jFxqbXJrfmNLlO2j1apagGGGSyZJhFnIuHp4I0=");
var serverUrl = TestHelpers.GetValue("config:server:url", "https://localhost:5001");
ClientManager = new SquidexClientManager(new SquidexOptions
{
@ -61,28 +83,49 @@ namespace TestSuite
ReadResponseAsString = true,
Url = serverUrl
});
}
public static Task<ClientManagerWrapper> CreateAsync()
apps = new Lazy<IAppsClient>(() =>
{
if (manager == null)
return ClientManager.CreateAppsClient();
});
assets = new Lazy<IAssetsClient>(() =>
{
manager = CreateInternalAsync();
}
return ClientManager.CreateAssetsClient();
});
return manager;
}
backups = new Lazy<IBackupsClient>(() =>
{
return ClientManager.CreateBackupsClient();
});
private static async Task<ClientManagerWrapper> CreateInternalAsync()
languages = new Lazy<ILanguagesClient>(() =>
{
var clientManager = new ClientManagerWrapper();
return ClientManager.CreateLanguagesClient();
});
await clientManager.ConnectAsync();
ping = new Lazy<IPingClient>(() =>
{
return ClientManager.CreatePingClient();
});
return clientManager;
rules = new Lazy<IRulesClient>(() =>
{
return ClientManager.CreateRulesClient();
});
schemas = new Lazy<ISchemasClient>(() =>
{
return ClientManager.CreateSchemasClient();
});
templates = new Lazy<ITemplatesClient>(() =>
{
return ClientManager.CreateTemplatesClient();
});
}
public async Task ConnectAsync()
public async Task<ClientManagerWrapper> ConnectAsync()
{
var waitSeconds = TestHelpers.Configuration.GetValue<int>("config:wait");
@ -108,27 +151,15 @@ namespace TestSuite
}
}
}
}
else
{
Console.WriteLine("Waiting for server is skipped.");
}
}
private static string GetValue(string name, string fallback)
{
var value = TestHelpers.Configuration[name];
if (string.IsNullOrWhiteSpace(value))
{
value = fallback;
Console.WriteLine("Connected to server.");
}
else
{
Console.WriteLine("Using: {0}={1}", name, value);
Console.WriteLine("Waiting for server is skipped.");
}
return value;
return this;
}
}
}

21
backend/tools/TestSuite/TestSuite.Shared/Factories.cs

@ -0,0 +1,21 @@
// ==========================================================================
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex UG (haftungsbeschraenkt)
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System.Collections.Concurrent;
namespace TestSuite
{
public static class Factories
{
private static readonly ConcurrentDictionary<string, Task<object>> Instances = new ConcurrentDictionary<string, Task<object>>();
public static async Task<T> CreateAsync<T>(string key, Func<Task<T>> factory)
{
return (T)await Instances.GetOrAdd(key, async (_, f) => await f(), factory);
}
}
}

2
backend/tools/TestSuite/TestSuite.Shared/Fixtures/ClientFixture.cs

@ -13,6 +13,8 @@ namespace TestSuite.Fixtures
{
public IAppsClient Apps => Squidex.Apps;
public IAssetsClient Assets => Squidex.Assets;
public IBackupsClient Backups => Squidex.Backups;
public ILanguagesClient Languages => Squidex.Languages;

20
backend/tools/TestSuite/TestSuite.Shared/Fixtures/ClientManagerFixture.cs

@ -6,12 +6,13 @@
// ==========================================================================
using Squidex.ClientLibrary;
using Xunit;
namespace TestSuite.Fixtures
{
public class ClientManagerFixture : IDisposable
public class ClientManagerFixture : IAsyncLifetime
{
public ClientManagerWrapper Squidex { get; }
public ClientManagerWrapper Squidex { get; private set; }
public string AppName => ClientManager.Options.AppName;
@ -23,14 +24,21 @@ namespace TestSuite.Fixtures
public SquidexClientManager ClientManager => Squidex.ClientManager;
public ClientManagerFixture()
public virtual async Task InitializeAsync()
{
Squidex = ClientManagerWrapper.CreateAsync().Result;
Squidex = await Factories.CreateAsync(nameof(ClientManagerWrapper), async () =>
{
var clientManager = new ClientManagerWrapper();
await clientManager.ConnectAsync();
return clientManager;
});
}
public virtual void Dispose()
public virtual Task DisposeAsync()
{
GC.SuppressFinalize(this);
return Task.CompletedTask;
}
}
}

77
backend/tools/TestSuite/TestSuite.Shared/Fixtures/ContentQueryFixture1to10.cs

@ -1,77 +0,0 @@
// ==========================================================================
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex UG (haftungsbeschraenkt)
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System.Globalization;
using Newtonsoft.Json.Linq;
using Squidex.ClientLibrary;
using TestSuite.Model;
namespace TestSuite.Fixtures
{
public class ContentQueryFixture1to10 : ContentFixture
{
public ContentQueryFixture1to10()
: this("my-reads")
{
}
protected ContentQueryFixture1to10(string schemaName = "my-schema")
: base(schemaName)
{
Task.Run(async () =>
{
#pragma warning disable MA0056 // Do not call overridable members in constructor
Dispose();
#pragma warning restore MA0056 // Do not call overridable members in constructor
for (var i = 10; i > 0; i--)
{
var text = i.ToString(CultureInfo.InvariantCulture);
var data = new TestEntityData
{
String = text,
Json = JObject.FromObject(new
{
nested1 = new
{
nested2 = i
}
}),
Number = i,
};
if (i % 2 == 0)
{
data.Geo = new { type = "Point", coordinates = new[] { i, i } };
}
else
{
data.Geo = new { longitude = i, latitude = i };
}
await Contents.CreateAsync(data, ContentCreateOptions.AsPublish);
}
}).Wait();
}
public override void Dispose()
{
Task.Run(async () =>
{
var contents = await Contents.GetAsync();
foreach (var content in contents.Items)
{
await Contents.DeleteAsync(content);
}
}).Wait();
GC.SuppressFinalize(this);
}
}
}

32
backend/tools/TestSuite/TestSuite.Shared/Fixtures/CreatedAppFixture.cs

@ -11,22 +11,18 @@ namespace TestSuite.Fixtures
{
public class CreatedAppFixture : ClientFixture
{
private static readonly string[] Contributors =
public override async Task InitializeAsync()
{
"hello@squidex.io"
};
await base.InitializeAsync();
private static bool isCreated;
public CreatedAppFixture()
{
if (!isCreated)
{
Task.Run(async () =>
await Factories.CreateAsync(AppName, async () =>
{
try
{
await Apps.PostAppAsync(new CreateAppDto { Name = AppName });
await Apps.PostAppAsync(new CreateAppDto
{
Name = AppName
});
}
catch (SquidexManagementException ex)
{
@ -36,15 +32,6 @@ namespace TestSuite.Fixtures
}
}
var invite = new AssignContributorDto { Invite = true, Role = "Owner" };
foreach (var contributor in Contributors)
{
invite.ContributorId = contributor;
await Apps.PostContributorAsync(AppName, invite);
}
try
{
await Apps.PostLanguageAsync(AppName, new AddLanguageDto
@ -59,10 +46,9 @@ namespace TestSuite.Fixtures
throw;
}
}
}).Wait();
isCreated = true;
}
return true;
});
}
}
}

25
backend/tools/TestSuite/TestSuite.Shared/Fixtures/ContentFixture.cs → backend/tools/TestSuite/TestSuite.Shared/Fixtures/TestSchemaFixtureBase.cs

@ -11,30 +11,26 @@ using TestSuite.Model;
namespace TestSuite.Fixtures
{
public class ContentFixture : CreatedAppFixture
public abstract class TestSchemaFixtureBase : CreatedAppFixture
{
private static readonly HashSet<string> CreatedSchemas = new HashSet<string>();
public IContentsClient<TestEntity, TestEntityData> Contents { get; }
public IContentsClient<TestEntity, TestEntityData> Contents { get; private set; }
public string SchemaName { get; }
public ContentFixture()
: this("my-writes")
protected TestSchemaFixtureBase(string schemaName)
{
SchemaName = schemaName;
}
protected ContentFixture(string schemaName)
public override async Task InitializeAsync()
{
SchemaName = schemaName;
await base.InitializeAsync();
if (!CreatedSchemas.Contains(schemaName))
{
Task.Run(async () =>
await Factories.CreateAsync($"{nameof(TestEntity)}_{SchemaName}", async () =>
{
try
{
await TestEntity.CreateSchemaAsync(Schemas, AppName, schemaName);
await TestEntity.CreateSchemaAsync(Schemas, AppName, SchemaName);
}
catch (SquidexManagementException ex)
{
@ -43,10 +39,9 @@ namespace TestSuite.Fixtures
throw;
}
}
}).Wait();
CreatedSchemas.Add(schemaName);
}
return true;
});
Contents = ClientManager.CreateContentsClient<TestEntity, TestEntityData>(SchemaName);
}

21
backend/tools/TestSuite/TestSuite.Shared/Fixtures/ContentReferencesFixture.cs → backend/tools/TestSuite/TestSuite.Shared/Fixtures/TestSchemaWithReferencesFixtureBase.cs

@ -11,15 +11,22 @@ using TestSuite.Model;
namespace TestSuite.Fixtures
{
public sealed class ContentReferencesFixture : CreatedAppFixture
public abstract class TestSchemaWithReferencesFixtureBase : CreatedAppFixture
{
public string SchemaName { get; } = "references";
public IContentsClient<TestEntityWithReferences, TestEntityWithReferencesData> Contents { get; private set; }
public IContentsClient<TestEntityWithReferences, TestEntityWithReferencesData> Contents { get; }
public string SchemaName { get; }
public ContentReferencesFixture()
protected TestSchemaWithReferencesFixtureBase(string schemaName)
{
Task.Run(async () =>
SchemaName = schemaName;
}
public override async Task InitializeAsync()
{
await base.InitializeAsync();
await Factories.CreateAsync($"{nameof(TestEntityWithReferences)}_{SchemaName}", async () =>
{
try
{
@ -32,7 +39,9 @@ namespace TestSuite.Fixtures
throw;
}
}
}).Wait();
return true;
});
Contents = ClientManager.CreateContentsClient<TestEntityWithReferences, TestEntityWithReferencesData>(SchemaName);
}

3
backend/tools/TestSuite/TestSuite.Shared/FodyWeavers.xml

@ -1,3 +0,0 @@
<Weavers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FodyWeavers.xsd">
<Lazy />
</Weavers>

37
backend/tools/TestSuite/TestSuite.Shared/Model/TestEntity.cs

@ -5,6 +5,7 @@
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System.Globalization;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Squidex.ClientLibrary;
@ -81,6 +82,42 @@ namespace TestSuite.Model
return schema;
}
public static TestEntityData CreateTestEntry(int index)
{
var data = new TestEntityData
{
Number = index,
Json = JObject.FromObject(new
{
nested0 = index,
nested1 = new
{
nested2 = index
}
}),
String = index.ToString(CultureInfo.InvariantCulture)
};
if (index % 2 == 0)
{
data.Geo = new
{
type = "Point",
coordinates = new[]
{
index,
index
}
};
}
else
{
data.Geo = new { longitude = index, latitude = index };
}
return data;
}
}
public sealed class TestEntityData

5
backend/tools/TestSuite/TestSuite.Shared/TestSuite.Shared.csproj

@ -6,11 +6,6 @@
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Fody" Version="6.6.0">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Lazy.Fody" Version="1.11.0" PrivateAssets="all" />
<PackageReference Include="Meziantou.Analyzer" Version="1.0.698">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>

47
backend/tools/TestSuite/TestSuite.Shared/Utils/RandomHash.cs

@ -1,47 +0,0 @@
// ==========================================================================
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex UG (haftungsbeschraenkt)
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System.Security.Cryptography;
using System.Text;
namespace TestSuite.Utils
{
public static class RandomHash
{
public static string New()
{
return Guid.NewGuid()
.ToString().Sha256Base64()
.ToLowerInvariant()
.Replace("+", "x", StringComparison.Ordinal)
.Replace("=", "x", StringComparison.Ordinal)
.Replace("/", "x", StringComparison.Ordinal);
}
public static string Simple()
{
return Guid.NewGuid().ToString().Replace("-", string.Empty, StringComparison.Ordinal);
}
public static string Sha256Base64(this string value)
{
return Sha256Base64(Encoding.UTF8.GetBytes(value));
}
public static string Sha256Base64(this byte[] bytes)
{
using (var sha = SHA256.Create())
{
var bytesHash = sha.ComputeHash(bytes);
var result = Convert.ToBase64String(bytesHash);
return result;
}
}
}
}

68
backend/tools/TestSuite/TestSuite.Shared/Utils/Run.cs

@ -1,68 +0,0 @@
// ==========================================================================
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex UG (haftungsbeschraenkt)
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System.Collections.Concurrent;
using System.Diagnostics;
using Xunit;
namespace TestSuite.Utils
{
public static class Run
{
public static async Task Parallel(int numUsers, int numIterationsPerUser, Func<Task> action, int expectedAvg = 100)
{
var elapsedMs = new ConcurrentBag<long>();
var errors = 0;
async Task RunAsync()
{
for (var i = 0; i < numIterationsPerUser; i++)
{
try
{
var watch = Stopwatch.StartNew();
await action();
watch.Stop();
elapsedMs.Add(watch.ElapsedMilliseconds);
}
catch
{
Interlocked.Increment(ref errors);
}
}
}
var tasks = new List<Task>();
for (var i = 0; i < numUsers; i++)
{
tasks.Add(Task.Run(RunAsync));
}
await Task.WhenAll(tasks);
var count = elapsedMs.Count;
var max = elapsedMs.Max();
var min = elapsedMs.Min();
var avg = elapsedMs.Average();
Assert.Equal(0, errors);
Assert.Equal(count, numUsers * numIterationsPerUser);
Assert.InRange(max, 0, expectedAvg * 10);
Assert.InRange(min, 0, expectedAvg);
Assert.InRange(avg, 0, expectedAvg);
}
}
}

16
backend/tools/TestSuite/TestSuite.Shared/Utils/TestHelpers.cs

@ -24,5 +24,21 @@ namespace TestSuite.Utils
.AddEnvironmentVariables()
.Build();
}
public static string GetValue(string name, string fallback)
{
var value = Configuration[name];
if (string.IsNullOrWhiteSpace(value))
{
value = fallback;
}
else
{
Console.WriteLine("Using: {0}={1}", name, value);
}
return value;
}
}
}

Loading…
Cancel
Save