mirror of https://github.com/Squidex/squidex.git
7 changed files with 184 additions and 16 deletions
@ -0,0 +1,168 @@ |
|||||
|
// ==========================================================================
|
||||
|
// Squidex Headless CMS
|
||||
|
// ==========================================================================
|
||||
|
// Copyright (c) Squidex UG (haftungsbeschränkt)
|
||||
|
// All rights reserved. Licensed under the MIT license.
|
||||
|
// ==========================================================================
|
||||
|
|
||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using System.Threading.Tasks; |
||||
|
using FakeItEasy; |
||||
|
using Microsoft.AspNetCore.Http; |
||||
|
using Microsoft.AspNetCore.Mvc; |
||||
|
using Microsoft.AspNetCore.Mvc.Abstractions; |
||||
|
using Microsoft.AspNetCore.Mvc.Filters; |
||||
|
using Microsoft.AspNetCore.Mvc.Infrastructure; |
||||
|
using Microsoft.AspNetCore.Routing; |
||||
|
using Squidex.Domain.Apps.Entities.Apps; |
||||
|
using Squidex.Domain.Apps.Entities.Apps.Services; |
||||
|
using Squidex.Infrastructure.UsageTracking; |
||||
|
using Squidex.Pipeline; |
||||
|
using Xunit; |
||||
|
|
||||
|
namespace Squidex.Tests.Pipeline |
||||
|
{ |
||||
|
public class ApiCostsFilterTests |
||||
|
{ |
||||
|
private readonly IActionContextAccessor actionContextAccessor = A.Fake<IActionContextAccessor>(); |
||||
|
private readonly IAppEntity appEntity = A.Fake<IAppEntity>(); |
||||
|
private readonly IAppPlansProvider appPlanProvider = A.Fake<IAppPlansProvider>(); |
||||
|
private readonly IUsageTracker usageTracker = A.Fake<IUsageTracker>(); |
||||
|
private readonly IAppLimitsPlan appPlan = A.Fake<IAppLimitsPlan>(); |
||||
|
private readonly ActionExecutingContext actionContext; |
||||
|
private readonly HttpContext httpContext = new DefaultHttpContext(); |
||||
|
private readonly ActionExecutionDelegate next; |
||||
|
private readonly ApiCostsFilter sut; |
||||
|
private long apiCallsMax; |
||||
|
private long apiCallsCurrent; |
||||
|
private bool isNextCalled; |
||||
|
|
||||
|
public ApiCostsFilterTests() |
||||
|
{ |
||||
|
actionContext = |
||||
|
new ActionExecutingContext( |
||||
|
new ActionContext(httpContext, new RouteData(), |
||||
|
new ActionDescriptor()), |
||||
|
new List<IFilterMetadata>(), new Dictionary<string, object>(), null); |
||||
|
|
||||
|
A.CallTo(() => actionContextAccessor.ActionContext) |
||||
|
.Returns(actionContext); |
||||
|
|
||||
|
A.CallTo(() => appPlanProvider.GetPlan(null)) |
||||
|
.Returns(appPlan); |
||||
|
|
||||
|
A.CallTo(() => appPlanProvider.GetPlanForApp(appEntity)) |
||||
|
.Returns(appPlan); |
||||
|
|
||||
|
A.CallTo(() => appPlan.MaxApiCalls) |
||||
|
.ReturnsLazily(x => apiCallsMax); |
||||
|
|
||||
|
A.CallTo(() => usageTracker.GetMonthlyCallsAsync(A<string>.Ignored, DateTime.Today)) |
||||
|
.ReturnsLazily(x => Task.FromResult(apiCallsCurrent)); |
||||
|
|
||||
|
next = () => |
||||
|
{ |
||||
|
isNextCalled = true; |
||||
|
|
||||
|
return Task.FromResult<ActionExecutedContext>(null); |
||||
|
}; |
||||
|
|
||||
|
sut = new ApiCostsFilter(appPlanProvider, usageTracker); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public async Task Should_return_429_status_code_if_max_calls_over_limit() |
||||
|
{ |
||||
|
sut.FilterDefinition = new ApiCostsAttribute(1); |
||||
|
|
||||
|
SetupApp(); |
||||
|
|
||||
|
apiCallsCurrent = 1000; |
||||
|
apiCallsMax = 600; |
||||
|
|
||||
|
await sut.OnActionExecutionAsync(actionContext, next); |
||||
|
|
||||
|
Assert.Equal(429, (actionContext.Result as StatusCodeResult).StatusCode); |
||||
|
Assert.False(isNextCalled); |
||||
|
|
||||
|
A.CallTo(() => usageTracker.TrackAsync(A<string>.Ignored, A<double>.Ignored, A<double>.Ignored)) |
||||
|
.MustNotHaveHappened(); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public async Task Should_track_if_calls_left() |
||||
|
{ |
||||
|
sut.FilterDefinition = new ApiCostsAttribute(13); |
||||
|
|
||||
|
SetupApp(); |
||||
|
|
||||
|
apiCallsCurrent = 1000; |
||||
|
apiCallsMax = 1600; |
||||
|
|
||||
|
await sut.OnActionExecutionAsync(actionContext, next); |
||||
|
|
||||
|
Assert.True(isNextCalled); |
||||
|
|
||||
|
A.CallTo(() => usageTracker.TrackAsync(A<string>.Ignored, 13, A<double>.Ignored)) |
||||
|
.MustHaveHappened(); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public async Task Should_allow_small_buffer() |
||||
|
{ |
||||
|
sut.FilterDefinition = new ApiCostsAttribute(13); |
||||
|
|
||||
|
SetupApp(); |
||||
|
|
||||
|
apiCallsCurrent = 1099; |
||||
|
apiCallsMax = 1000; |
||||
|
|
||||
|
await sut.OnActionExecutionAsync(actionContext, next); |
||||
|
|
||||
|
Assert.True(isNextCalled); |
||||
|
|
||||
|
A.CallTo(() => usageTracker.TrackAsync(A<string>.Ignored, 13, A<double>.Ignored)) |
||||
|
.MustHaveHappened(); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public async Task Should_not_track_if_weight_is_zero() |
||||
|
{ |
||||
|
sut.FilterDefinition = new ApiCostsAttribute(0); |
||||
|
|
||||
|
SetupApp(); |
||||
|
|
||||
|
apiCallsCurrent = 1000; |
||||
|
apiCallsMax = 600; |
||||
|
|
||||
|
await sut.OnActionExecutionAsync(actionContext, next); |
||||
|
|
||||
|
Assert.True(isNextCalled); |
||||
|
|
||||
|
A.CallTo(() => usageTracker.TrackAsync(A<string>.Ignored, A<double>.Ignored, A<double>.Ignored)) |
||||
|
.MustNotHaveHappened(); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public async Task Should_not_track_if_app_not_defined() |
||||
|
{ |
||||
|
sut.FilterDefinition = new ApiCostsAttribute(1); |
||||
|
|
||||
|
apiCallsCurrent = 1000; |
||||
|
apiCallsMax = 600; |
||||
|
|
||||
|
await sut.OnActionExecutionAsync(actionContext, next); |
||||
|
|
||||
|
Assert.True(isNextCalled); |
||||
|
|
||||
|
A.CallTo(() => usageTracker.TrackAsync(A<string>.Ignored, A<double>.Ignored, A<double>.Ignored)) |
||||
|
.MustNotHaveHappened(); |
||||
|
} |
||||
|
|
||||
|
private void SetupApp() |
||||
|
{ |
||||
|
httpContext.Features.Set<IAppFeature>(new AppApiFilter.AppFeature(appEntity)); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
Loading…
Reference in new issue