mirror of https://github.com/Squidex/squidex.git
7 changed files with 329 additions and 0 deletions
@ -0,0 +1,75 @@ |
|||
// ==========================================================================
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex UG (haftungsbeschraenkt)
|
|||
// All rights reserved. Licensed under the MIT license.
|
|||
// ==========================================================================
|
|||
|
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Threading.Tasks; |
|||
using Squidex.ClientLibrary; |
|||
using Squidex.ClientLibrary.Management; |
|||
|
|||
namespace LoadTest |
|||
{ |
|||
public sealed class ClientQueryFixture : IDisposable |
|||
{ |
|||
public SquidexClient<TestEntity, TestEntityData> Client { get; } = TestClient.Build(); |
|||
|
|||
public ClientQueryFixture() |
|||
{ |
|||
Task.Run(async () => |
|||
{ |
|||
var apps = TestClient.ClientManager.CreateAppsClient(); |
|||
|
|||
try |
|||
{ |
|||
await apps.PostAppAsync(new CreateAppDto |
|||
{ |
|||
Name = TestClient.TestAppName |
|||
}); |
|||
|
|||
var schemas = TestClient.ClientManager.CreateSchemasClient(); |
|||
|
|||
await schemas.PostSchemaAsync(TestClient.TestAppName, new CreateSchemaDto |
|||
{ |
|||
Name = TestClient.TestSchemaName, |
|||
Fields = new List<UpsertSchemaFieldDto> |
|||
{ |
|||
new UpsertSchemaFieldDto |
|||
{ |
|||
Name = TestClient.TestSchemaName, |
|||
Properties = new NumberFieldPropertiesDto() |
|||
} |
|||
}, |
|||
IsPublished = true |
|||
}); |
|||
} |
|||
catch (SquidexManagementException ex) |
|||
{ |
|||
if (ex.StatusCode != 400) |
|||
{ |
|||
throw; |
|||
} |
|||
} |
|||
|
|||
var contents = await Client.GetAllAsync(); |
|||
|
|||
foreach (var content in contents.Items) |
|||
{ |
|||
await Client.DeleteAsync(content); |
|||
} |
|||
|
|||
for (var i = 10; i > 0; i--) |
|||
{ |
|||
await Client.CreateAsync(new TestEntityData { Value = i }, true); |
|||
} |
|||
}).Wait(); |
|||
} |
|||
|
|||
public void Dispose() |
|||
{ |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,23 @@ |
|||
<Project Sdk="Microsoft.NET.Sdk"> |
|||
<PropertyGroup> |
|||
<OutputType>Exe</OutputType> |
|||
<TargetFramework>netcoreapp2.2</TargetFramework> |
|||
</PropertyGroup> |
|||
<ItemGroup> |
|||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.2.0" /> |
|||
<PackageReference Include="RefactoringEssentials" Version="5.6.0" PrivateAssets="all" /> |
|||
<PackageReference Include="Squidex.ClientLibrary" Version="3.9.0" /> |
|||
<PackageReference Include="StyleCop.Analyzers" Version="1.1.118" PrivateAssets="all" /> |
|||
<PackageReference Include="xunit" Version="2.4.1" /> |
|||
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.1"> |
|||
<PrivateAssets>all</PrivateAssets> |
|||
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets> |
|||
</PackageReference> |
|||
</ItemGroup> |
|||
<PropertyGroup> |
|||
<CodeAnalysisRuleSet>..\..\Squidex.ruleset</CodeAnalysisRuleSet> |
|||
</PropertyGroup> |
|||
<ItemGroup> |
|||
<AdditionalFiles Include="..\..\stylecop.json" Link="stylecop.json" /> |
|||
</ItemGroup> |
|||
</Project> |
|||
@ -0,0 +1,25 @@ |
|||
|
|||
Microsoft Visual Studio Solution File, Format Version 12.00 |
|||
# Visual Studio Version 16 |
|||
VisualStudioVersion = 16.0.29123.88 |
|||
MinimumVisualStudioVersion = 10.0.40219.1 |
|||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LoadTest", "LoadTest.csproj", "{EC732B4F-576E-4853-880D-AA676966D017}" |
|||
EndProject |
|||
Global |
|||
GlobalSection(SolutionConfigurationPlatforms) = preSolution |
|||
Debug|Any CPU = Debug|Any CPU |
|||
Release|Any CPU = Release|Any CPU |
|||
EndGlobalSection |
|||
GlobalSection(ProjectConfigurationPlatforms) = postSolution |
|||
{EC732B4F-576E-4853-880D-AA676966D017}.Debug|Any CPU.ActiveCfg = Debug|Any CPU |
|||
{EC732B4F-576E-4853-880D-AA676966D017}.Debug|Any CPU.Build.0 = Debug|Any CPU |
|||
{EC732B4F-576E-4853-880D-AA676966D017}.Release|Any CPU.ActiveCfg = Release|Any CPU |
|||
{EC732B4F-576E-4853-880D-AA676966D017}.Release|Any CPU.Build.0 = Release|Any CPU |
|||
EndGlobalSection |
|||
GlobalSection(SolutionProperties) = preSolution |
|||
HideSolutionNode = FALSE |
|||
EndGlobalSection |
|||
GlobalSection(ExtensibilityGlobals) = postSolution |
|||
SolutionGuid = {8ABC073D-D6C6-47F0-AFB6-D42F177D468A} |
|||
EndGlobalSection |
|||
EndGlobal |
|||
@ -0,0 +1,146 @@ |
|||
// ==========================================================================
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex UG (haftungsbeschraenkt)
|
|||
// All rights reserved. Licensed under the MIT license.
|
|||
// ==========================================================================
|
|||
|
|||
using System; |
|||
using System.Collections.Concurrent; |
|||
using System.Collections.Generic; |
|||
using System.Diagnostics; |
|||
using System.Linq; |
|||
using System.Threading; |
|||
using System.Threading.Tasks; |
|||
using Squidex.ClientLibrary; |
|||
using Xunit; |
|||
|
|||
namespace LoadTest |
|||
{ |
|||
public class QueryBenchmarks : IClassFixture<ClientQueryFixture> |
|||
{ |
|||
public ClientQueryFixture Fixture { get; } |
|||
|
|||
public QueryBenchmarks(ClientQueryFixture fixture) |
|||
{ |
|||
Fixture = fixture; |
|||
} |
|||
|
|||
public static IEnumerable<object[]> Loads() |
|||
{ |
|||
int[] users = { 1, 5, 10, 20, 50, 100 }; |
|||
int[] loads = { 5, 10, 20, 50, 100 }; |
|||
|
|||
foreach (var user in users) |
|||
{ |
|||
foreach (var load in loads) |
|||
{ |
|||
yield return new object[] { user, load }; |
|||
} |
|||
} |
|||
} |
|||
|
|||
[Theory] |
|||
[MemberData(nameof(Loads))] |
|||
public async Task Should_return_all(int numUsers, int numIterationsPerUser) |
|||
{ |
|||
await Run(numUsers, numIterationsPerUser, async () => |
|||
{ |
|||
await Fixture.Client.GetAsync(new ODataQuery { OrderBy = "data/value/iv asc" }); |
|||
}); |
|||
} |
|||
|
|||
[Theory] |
|||
[MemberData(nameof(Loads))] |
|||
public async Task Should_return_items_with_skip(int numUsers, int numIterationsPerUser) |
|||
{ |
|||
await Run(numUsers, numIterationsPerUser, async () => |
|||
{ |
|||
await Fixture.Client.GetAsync(new ODataQuery { Skip = 5, OrderBy = "data/value/iv asc" }); |
|||
}); |
|||
} |
|||
|
|||
[Theory] |
|||
[MemberData(nameof(Loads))] |
|||
public async Task Should_return_items_with_skip_and_top(int numUsers, int numIterationsPerUser) |
|||
{ |
|||
await Run(numUsers, numIterationsPerUser, async () => |
|||
{ |
|||
await Fixture.Client.GetAsync(new ODataQuery { Skip = 2, Top = 5, OrderBy = "data/value/iv asc" }); |
|||
}); |
|||
} |
|||
|
|||
[Theory] |
|||
[MemberData(nameof(Loads))] |
|||
public async Task Should_return_items_with_ordering(int numUsers, int numIterationsPerUser) |
|||
{ |
|||
await Run(numUsers, numIterationsPerUser, async () => |
|||
{ |
|||
await Fixture.Client.GetAsync(new ODataQuery { Skip = 2, Top = 5, OrderBy = "data/value/iv desc" }); |
|||
}); |
|||
} |
|||
|
|||
[Theory] |
|||
[MemberData(nameof(Loads))] |
|||
public async Task Should_return_items_with_filter(int numUsers, int numIterationsPerUser) |
|||
{ |
|||
await Run(numUsers, numIterationsPerUser, async () => |
|||
{ |
|||
await Fixture.Client.GetAsync(new ODataQuery { Filter = "data/value/iv gt 3 and data/value/iv lt 7", OrderBy = "data/value/iv asc" }); |
|||
}); |
|||
} |
|||
|
|||
private static async Task Run(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.InRange(max, 0, expectedAvg * 10); |
|||
Assert.InRange(min, 0, expectedAvg); |
|||
|
|||
Assert.InRange(avg, 0, expectedAvg); |
|||
|
|||
Assert.Equal(0, errors); |
|||
|
|||
Assert.Equal(count, numUsers * numIterationsPerUser); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,34 @@ |
|||
// ==========================================================================
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex UG (haftungsbeschraenkt)
|
|||
// All rights reserved. Licensed under the MIT license.
|
|||
// ==========================================================================
|
|||
|
|||
using Squidex.ClientLibrary; |
|||
|
|||
namespace LoadTest |
|||
{ |
|||
public static class TestClient |
|||
{ |
|||
public const string ServerUrl = "http://localhost:5000"; |
|||
|
|||
public const string ClientId = "root"; |
|||
public const string ClientSecret = "xeLd6jFxqbXJrfmNLlO2j1apagGGGSyZJhFnIuHp4I0="; |
|||
|
|||
public const string TestAppName = "integration-tests"; |
|||
public const string TestSchemaName = "numbers"; |
|||
public const string TestSchemaField = "value"; |
|||
|
|||
public static readonly SquidexClientManager ClientManager = |
|||
new SquidexClientManager("http://localhost:5000", TestAppName, ClientId, ClientSecret) |
|||
{ |
|||
ReadResponseAsString = true |
|||
}; |
|||
|
|||
public static SquidexClient<TestEntity, TestEntityData> Build() |
|||
{ |
|||
return ClientManager.GetClient<TestEntity, TestEntityData>(TestSchemaName); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,22 @@ |
|||
// ==========================================================================
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex UG (haftungsbeschraenkt)
|
|||
// All rights reserved. Licensed under the MIT license.
|
|||
// ==========================================================================
|
|||
|
|||
using Newtonsoft.Json; |
|||
using Squidex.ClientLibrary; |
|||
|
|||
namespace LoadTest |
|||
{ |
|||
public sealed class TestEntity : SquidexEntityBase<TestEntityData> |
|||
{ |
|||
} |
|||
|
|||
public sealed class TestEntityData |
|||
{ |
|||
[JsonConverter(typeof(InvariantConverter))] |
|||
public int Value { get; set; } |
|||
} |
|||
} |
|||
Loading…
Reference in new issue