Browse Source

Something went wrong with last commit.

pull/315/head
Sebastian Stehle 7 years ago
parent
commit
3beb7d07f5
  1. 4
      src/Squidex.Infrastructure.MongoDb/UsageTracking/MongoUsage.cs
  2. 13
      src/Squidex.Infrastructure.MongoDb/UsageTracking/MongoUsageStore.cs
  3. 60
      src/Squidex.Infrastructure/UsageTracking/BackgroundUsageTracker.cs
  4. 6
      src/Squidex.Infrastructure/UsageTracking/CachingUsageTracker.cs
  5. 28
      src/Squidex.Infrastructure/UsageTracking/DateUsage.cs
  6. 2
      src/Squidex.Infrastructure/UsageTracking/IUsageStore.cs
  7. 4
      src/Squidex.Infrastructure/UsageTracking/IUsageTracker.cs
  8. 6
      src/Squidex.Infrastructure/UsageTracking/StoredUsage.cs
  9. 13
      tests/Squidex.Domain.Apps.Entities.Tests/Assets/AssetCommandMiddlewareTests.cs
  10. 4
      tests/Squidex.Domain.Users.Tests/AssetUserPictureStoreTests.cs
  11. 13
      tests/Squidex.Infrastructure.Tests/EventSourcing/CompoundEventConsumerTests.cs
  12. 3
      tests/Squidex.Infrastructure.Tests/Orleans/BootstrapTests.cs
  13. 130
      tests/Squidex.Infrastructure.Tests/UsageTracking/BackgroundUsageTrackerTests.cs
  14. 4
      tests/Squidex.Infrastructure.Tests/UsageTracking/ThreadingUsageTrackerTests.cs
  15. 10
      tests/Squidex.Tests/Pipeline/ApiCostsFilterTests.cs

4
src/Squidex.Infrastructure.MongoDb/UsageTracking/MongoUsage.cs

@ -27,6 +27,10 @@ namespace Squidex.Infrastructure.UsageTracking
[BsonElement] [BsonElement]
public string Key { get; set; } public string Key { get; set; }
[BsonIgnoreIfNull]
[BsonElement]
public string Category { get; set; }
[BsonRequired] [BsonRequired]
[BsonElement] [BsonElement]
public double TotalCount { get; set; } public double TotalCount { get; set; }

13
src/Squidex.Infrastructure.MongoDb/UsageTracking/MongoUsageStore.cs

@ -29,20 +29,21 @@ namespace Squidex.Infrastructure.UsageTracking
protected override Task SetupCollectionAsync(IMongoCollection<MongoUsage> collection) protected override Task SetupCollectionAsync(IMongoCollection<MongoUsage> collection)
{ {
return collection.Indexes.CreateOneAsync( return collection.Indexes.CreateOneAsync(
new CreateIndexModel<MongoUsage>(Index.Ascending(x => x.Key).Ascending(x => x.Date))); new CreateIndexModel<MongoUsage>(Index.Ascending(x => x.Key).Ascending(x => x.Category).Ascending(x => x.Date)));
} }
public Task TrackUsagesAsync(DateTime date, string key, double count, double elapsedMs) public Task TrackUsagesAsync(DateTime date, string key, string category, double count, double elapsedMs)
{ {
var id = $"{key}_{date:yyyy-MM-dd}"; var id = $"{key}_{date:yyyy-MM-dd}_{category}";
return Collection.UpdateOneAsync(x => x.Id == id, return Collection.UpdateOneAsync(x => x.Id == id && x.Category == category,
Update Update
.Inc(x => x.TotalCount, count) .Inc(x => x.TotalCount, count)
.Inc(x => x.TotalElapsedMs, elapsedMs) .Inc(x => x.TotalElapsedMs, elapsedMs)
.SetOnInsert(x => x.Id, id) .SetOnInsert(x => x.Id, id)
.SetOnInsert(x => x.Key, key) .SetOnInsert(x => x.Key, key)
.SetOnInsert(x => x.Date, date), .SetOnInsert(x => x.Date, date)
.SetOnInsert(x => x.Category, category),
Upsert); Upsert);
} }
@ -50,7 +51,7 @@ namespace Squidex.Infrastructure.UsageTracking
{ {
var entities = await Collection.Find(x => x.Key == key && x.Date >= fromDate && x.Date <= toDate).ToListAsync(); var entities = await Collection.Find(x => x.Key == key && x.Date >= fromDate && x.Date <= toDate).ToListAsync();
return entities.Select(x => new StoredUsage(x.Date, (long)x.TotalCount, (long)x.TotalElapsedMs)).ToList(); return entities.Select(x => new StoredUsage(x.Category, x.Date, (long)x.TotalCount, (long)x.TotalElapsedMs)).ToList();
} }
} }
} }

60
src/Squidex.Infrastructure/UsageTracking/BackgroundUsageTracker.cs

@ -19,11 +19,12 @@ namespace Squidex.Infrastructure.UsageTracking
{ {
public sealed class BackgroundUsageTracker : DisposableObjectBase, IUsageTracker public sealed class BackgroundUsageTracker : DisposableObjectBase, IUsageTracker
{ {
private const string FallbackCategory = "*";
private const int Intervall = 60 * 1000; private const int Intervall = 60 * 1000;
private readonly IUsageStore usageStore; private readonly IUsageStore usageStore;
private readonly ISemanticLog log; private readonly ISemanticLog log;
private readonly CompletionTimer timer; private readonly CompletionTimer timer;
private ConcurrentDictionary<string, Usage> usages = new ConcurrentDictionary<string, Usage>(); private ConcurrentDictionary<(string Key, string Category), Usage> usages = new ConcurrentDictionary<(string Key, string Category), Usage>();
public BackgroundUsageTracker(IUsageStore usageStore, ISemanticLog log) public BackgroundUsageTracker(IUsageStore usageStore, ISemanticLog log)
{ {
@ -58,12 +59,13 @@ namespace Squidex.Infrastructure.UsageTracking
{ {
var today = DateTime.Today; var today = DateTime.Today;
var localUsages = Interlocked.Exchange(ref usages, new ConcurrentDictionary<string, Usage>()); var localUsages = Interlocked.Exchange(ref usages, new ConcurrentDictionary<(string Key, string Category), Usage>());
await Task.WhenAll(localUsages.Select(x => await Task.WhenAll(localUsages.Select(x =>
usageStore.TrackUsagesAsync( usageStore.TrackUsagesAsync(
today, today,
x.Key, x.Key.Key,
x.Key.Category,
x.Value.Count, x.Value.Count,
x.Value.ElapsedMs))); x.Value.ElapsedMs)));
} }
@ -75,7 +77,7 @@ namespace Squidex.Infrastructure.UsageTracking
} }
} }
public Task TrackAsync(string key, double weight, double elapsedMs) public Task TrackAsync(string key, string category, double weight, double elapsedMs)
{ {
Guard.NotNull(key, nameof(key)); Guard.NotNull(key, nameof(key));
@ -83,29 +85,58 @@ namespace Squidex.Infrastructure.UsageTracking
if (weight > 0) if (weight > 0)
{ {
usages.AddOrUpdate(key, _ => new Usage(elapsedMs, weight), (k, x) => x.Add(elapsedMs, weight)); category = CleanCategory(category);
usages.AddOrUpdate((key, category), _ => new Usage(elapsedMs, weight), (k, x) => x.Add(elapsedMs, weight));
} }
return TaskHelper.Done; return TaskHelper.Done;
} }
public async Task<IReadOnlyList<StoredUsage>> QueryAsync(string key, DateTime fromDate, DateTime toDate) public async Task<IReadOnlyDictionary<string, IReadOnlyList<DateUsage>>> QueryAsync(string key, DateTime fromDate, DateTime toDate)
{ {
Guard.NotNull(key, nameof(key)); Guard.NotNull(key, nameof(key));
ThrowIfDisposed(); ThrowIfDisposed();
var originalUsages = await usageStore.QueryAsync(key, fromDate, toDate); var usagesFlat = await usageStore.QueryAsync(key, fromDate, toDate);
var enrichedUsages = new List<StoredUsage>(); var usagesByCategory = usagesFlat.GroupBy(x => CleanCategory(x.Category)).ToDictionary(x => x.Key, x => x.ToList());
var result = new Dictionary<string, IReadOnlyList<DateUsage>>();
var usagesDictionary = originalUsages.ToDictionary(x => x.Date); IEnumerable<string> categories = usagesByCategory.Keys;
for (var date = fromDate; date <= toDate; date = date.AddDays(1)) if (usagesByCategory.Count == 0)
{ {
enrichedUsages.Add(usagesDictionary.GetOrDefault(date) ?? new StoredUsage(date, 0, 0)); var enriched = new List<DateUsage>();
for (var date = fromDate; date <= toDate; date = date.AddDays(1))
{
enriched.Add(new DateUsage(date, 0, 0));
}
result[FallbackCategory] = enriched;
} }
else
{
foreach (var category in categories)
{
var enriched = new List<DateUsage>();
var usagesDictionary = usagesByCategory[category].ToDictionary(x => x.Date);
for (var date = fromDate; date <= toDate; date = date.AddDays(1))
{
var stored = usagesDictionary.GetOrDefault(date);
return enrichedUsages; enriched.Add(new DateUsage(date, stored?.TotalCount ?? 0, stored?.TotalElapsedMs ?? 0));
}
result[category] = enriched;
}
}
return result;
} }
public async Task<long> GetMonthlyCallsAsync(string key, DateTime date) public async Task<long> GetMonthlyCallsAsync(string key, DateTime date)
@ -121,5 +152,10 @@ namespace Squidex.Infrastructure.UsageTracking
return originalUsages.Sum(x => x.TotalCount); return originalUsages.Sum(x => x.TotalCount);
} }
private static string CleanCategory(string category)
{
return !string.IsNullOrWhiteSpace(category) ? category.Trim() : "*";
}
} }
} }

6
src/Squidex.Infrastructure/UsageTracking/CachingUsageTracker.cs

@ -25,14 +25,14 @@ namespace Squidex.Infrastructure.UsageTracking
this.inner = inner; this.inner = inner;
} }
public Task<IReadOnlyList<StoredUsage>> QueryAsync(string key, DateTime fromDate, DateTime toDate) public Task<IReadOnlyDictionary<string, IReadOnlyList<DateUsage>>> QueryAsync(string key, DateTime fromDate, DateTime toDate)
{ {
return inner.QueryAsync(key, fromDate, toDate); return inner.QueryAsync(key, fromDate, toDate);
} }
public Task TrackAsync(string key, double weight, double elapsedMs) public Task TrackAsync(string key, string category, double weight, double elapsedMs)
{ {
return inner.TrackAsync(key, weight, elapsedMs); return inner.TrackAsync(key, category, weight, elapsedMs);
} }
public Task<long> GetMonthlyCallsAsync(string key, DateTime date) public Task<long> GetMonthlyCallsAsync(string key, DateTime date)

28
src/Squidex.Infrastructure/UsageTracking/DateUsage.cs

@ -0,0 +1,28 @@
// ==========================================================================
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex UG (haftungsbeschraenkt)
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System;
namespace Squidex.Infrastructure.UsageTracking
{
public sealed class DateUsage
{
public DateTime Date { get; }
public long TotalCount { get; }
public long TotalElapsedMs { get; }
public DateUsage(DateTime date, long totalCount, long totalElapsedMs)
{
Date = date;
TotalCount = totalCount;
TotalElapsedMs = totalElapsedMs;
}
}
}

2
src/Squidex.Infrastructure/UsageTracking/IUsageStore.cs

@ -13,7 +13,7 @@ namespace Squidex.Infrastructure.UsageTracking
{ {
public interface IUsageStore public interface IUsageStore
{ {
Task TrackUsagesAsync(DateTime date, string key, double count, double elapsedMs); Task TrackUsagesAsync(DateTime date, string key, string category, double count, double elapsedMs);
Task<IReadOnlyList<StoredUsage>> QueryAsync(string key, DateTime fromDate, DateTime toDate); Task<IReadOnlyList<StoredUsage>> QueryAsync(string key, DateTime fromDate, DateTime toDate);
} }

4
src/Squidex.Infrastructure/UsageTracking/IUsageTracker.cs

@ -13,10 +13,10 @@ namespace Squidex.Infrastructure.UsageTracking
{ {
public interface IUsageTracker public interface IUsageTracker
{ {
Task TrackAsync(string key, double weight, double elapsedMs); Task TrackAsync(string key, string category, double weight, double elapsedMs);
Task<long> GetMonthlyCallsAsync(string key, DateTime date); Task<long> GetMonthlyCallsAsync(string key, DateTime date);
Task<IReadOnlyList<StoredUsage>> QueryAsync(string key, DateTime fromDate, DateTime toDate); Task<IReadOnlyDictionary<string, IReadOnlyList<DateUsage>>> QueryAsync(string key, DateTime fromDate, DateTime toDate);
} }
} }

6
src/Squidex.Infrastructure/UsageTracking/StoredUsage.cs

@ -11,14 +11,18 @@ namespace Squidex.Infrastructure.UsageTracking
{ {
public sealed class StoredUsage public sealed class StoredUsage
{ {
public string Category { get; }
public DateTime Date { get; } public DateTime Date { get; }
public long TotalCount { get; } public long TotalCount { get; }
public long TotalElapsedMs { get; } public long TotalElapsedMs { get; }
public StoredUsage(DateTime date, long totalCount, long totalElapsedMs) public StoredUsage(string category, DateTime date, long totalCount, long totalElapsedMs)
{ {
Category = category;
Date = date; Date = date;
TotalCount = totalCount; TotalCount = totalCount;

13
tests/Squidex.Domain.Apps.Entities.Tests/Assets/AssetCommandMiddlewareTests.cs

@ -19,7 +19,6 @@ using Squidex.Domain.Apps.Entities.TestHelpers;
using Squidex.Infrastructure.Assets; using Squidex.Infrastructure.Assets;
using Squidex.Infrastructure.Commands; using Squidex.Infrastructure.Commands;
using Squidex.Infrastructure.Log; using Squidex.Infrastructure.Log;
using Squidex.Infrastructure.Tasks;
using Xunit; using Xunit;
namespace Squidex.Domain.Apps.Entities.Assets namespace Squidex.Domain.Apps.Entities.Assets
@ -72,7 +71,6 @@ namespace Squidex.Domain.Apps.Entities.Assets
tags.Add("tag2"); tags.Add("tag2");
})); }));
SetupStore(0, context.ContextId);
SetupImageInfo(); SetupImageInfo();
await sut.HandleAsync(context); await sut.HandleAsync(context);
@ -92,7 +90,6 @@ namespace Squidex.Domain.Apps.Entities.Assets
{ {
var context = CreateContextForCommand(new UpdateAsset { AssetId = assetId, File = file }); var context = CreateContextForCommand(new UpdateAsset { AssetId = assetId, File = file });
SetupStore(1, context.ContextId);
SetupImageInfo(); SetupImageInfo();
await ExecuteCreateAsync(); await ExecuteCreateAsync();
@ -108,16 +105,6 @@ namespace Squidex.Domain.Apps.Entities.Assets
return asset.ExecuteAsync(CreateCommand(new CreateAsset { AssetId = Id, File = file })); return asset.ExecuteAsync(CreateCommand(new CreateAsset { AssetId = Id, File = file }));
} }
private void SetupStore(long version, Guid commitId)
{
A.CallTo(() => assetStore.UploadAsync(commitId.ToString(), stream, CancellationToken.None))
.Returns(TaskHelper.Done);
A.CallTo(() => assetStore.CopyAsync(commitId.ToString(), assetId.ToString(), version, null, CancellationToken.None))
.Returns(TaskHelper.Done);
A.CallTo(() => assetStore.DeleteAsync(commitId.ToString()))
.Returns(TaskHelper.Done);
}
private void AssertAssetHasBeenUploaded(long version, Guid commitId) private void AssertAssetHasBeenUploaded(long version, Guid commitId)
{ {
A.CallTo(() => assetStore.UploadAsync(commitId.ToString(), stream, CancellationToken.None)) A.CallTo(() => assetStore.UploadAsync(commitId.ToString(), stream, CancellationToken.None))

4
tests/Squidex.Domain.Users.Tests/AssetUserPictureStoreTests.cs

@ -12,7 +12,6 @@ using System.Threading.Tasks;
using FakeItEasy; using FakeItEasy;
using FakeItEasy.Core; using FakeItEasy.Core;
using Squidex.Infrastructure.Assets; using Squidex.Infrastructure.Assets;
using Squidex.Infrastructure.Tasks;
using Xunit; using Xunit;
#pragma warning disable RECS0165 // Asynchronous methods should return a Task instead of void #pragma warning disable RECS0165 // Asynchronous methods should return a Task instead of void
@ -35,9 +34,6 @@ namespace Squidex.Domain.Users
{ {
var stream = new MemoryStream(); var stream = new MemoryStream();
A.CallTo(() => assetStore.UploadAsync(userId, 0, "picture", stream, CancellationToken.None))
.Returns(TaskHelper.Done);
await sut.UploadAsync(userId, stream); await sut.UploadAsync(userId, stream);
A.CallTo(() => assetStore.UploadAsync(userId, 0, "picture", stream, CancellationToken.None)).MustHaveHappened(); A.CallTo(() => assetStore.UploadAsync(userId, 0, "picture", stream, CancellationToken.None)).MustHaveHappened();

13
tests/Squidex.Infrastructure.Tests/EventSourcing/CompoundEventConsumerTests.cs

@ -7,7 +7,6 @@
using System.Threading.Tasks; using System.Threading.Tasks;
using FakeItEasy; using FakeItEasy;
using Squidex.Infrastructure.Tasks;
using Squidex.Infrastructure.TestHelpers; using Squidex.Infrastructure.TestHelpers;
using Xunit; using Xunit;
@ -72,12 +71,6 @@ namespace Squidex.Infrastructure.EventSourcing
[Fact] [Fact]
public async Task Should_clear_all_consumers() public async Task Should_clear_all_consumers()
{ {
A.CallTo(() => consumer1.ClearAsync()).
Returns(TaskHelper.Done);
A.CallTo(() => consumer2.ClearAsync())
.Returns(TaskHelper.Done);
var sut = new CompoundEventConsumer("consumer-name", consumer1, consumer2); var sut = new CompoundEventConsumer("consumer-name", consumer1, consumer2);
await sut.ClearAsync(); await sut.ClearAsync();
@ -91,12 +84,6 @@ namespace Squidex.Infrastructure.EventSourcing
{ {
var @event = Envelope.Create(new MyEvent()); var @event = Envelope.Create(new MyEvent());
A.CallTo(() => consumer1.On(@event))
.Returns(TaskHelper.Done);
A.CallTo(() => consumer2.On(@event))
.Returns(TaskHelper.Done);
var sut = new CompoundEventConsumer("consumer-name", consumer1, consumer2); var sut = new CompoundEventConsumer("consumer-name", consumer1, consumer2);
await sut.On(@event); await sut.On(@event);

3
tests/Squidex.Infrastructure.Tests/Orleans/BootstrapTests.cs

@ -53,9 +53,6 @@ namespace Squidex.Infrastructure.Orleans
[Fact] [Fact]
public async Task Should_retry_after_rejection_exception() public async Task Should_retry_after_rejection_exception()
{ {
A.CallTo(() => grain.ActivateAsync())
.Returns(TaskHelper.Done);
A.CallTo(() => grain.ActivateAsync()) A.CallTo(() => grain.ActivateAsync())
.Throws(new OrleansException()).Once(); .Throws(new OrleansException()).Once();

130
tests/Squidex.Infrastructure.Tests/UsageTracking/BackgroundUsageTrackerTests.cs

@ -11,7 +11,6 @@ using System.Threading.Tasks;
using FakeItEasy; using FakeItEasy;
using FluentAssertions; using FluentAssertions;
using Squidex.Infrastructure.Log; using Squidex.Infrastructure.Log;
using Squidex.Infrastructure.Tasks;
using Xunit; using Xunit;
namespace Squidex.Infrastructure.UsageTracking namespace Squidex.Infrastructure.UsageTracking
@ -32,7 +31,7 @@ namespace Squidex.Infrastructure.UsageTracking
{ {
sut.Dispose(); sut.Dispose();
return Assert.ThrowsAsync<ObjectDisposedException>(() => sut.TrackAsync("key1", 1, 1000)); return Assert.ThrowsAsync<ObjectDisposedException>(() => sut.TrackAsync("MyKey1", "category1", 1, 1000));
} }
[Fact] [Fact]
@ -40,7 +39,7 @@ namespace Squidex.Infrastructure.UsageTracking
{ {
sut.Dispose(); sut.Dispose();
return Assert.ThrowsAsync<ObjectDisposedException>(() => sut.QueryAsync("key1", DateTime.Today, DateTime.Today.AddDays(1))); return Assert.ThrowsAsync<ObjectDisposedException>(() => sut.QueryAsync("MyKey1", DateTime.Today, DateTime.Today.AddDays(1)));
} }
[Fact] [Fact]
@ -48,7 +47,7 @@ namespace Squidex.Infrastructure.UsageTracking
{ {
sut.Dispose(); sut.Dispose();
return Assert.ThrowsAsync<ObjectDisposedException>(() => sut.GetMonthlyCallsAsync("key1", DateTime.Today)); return Assert.ThrowsAsync<ObjectDisposedException>(() => sut.GetMonthlyCallsAsync("MyKey1", DateTime.Today));
} }
[Fact] [Fact]
@ -58,16 +57,16 @@ namespace Squidex.Infrastructure.UsageTracking
IReadOnlyList<StoredUsage> originalData = new List<StoredUsage> IReadOnlyList<StoredUsage> originalData = new List<StoredUsage>
{ {
new StoredUsage(date.AddDays(1), 10, 15), new StoredUsage("category1", date.AddDays(1), 10, 15),
new StoredUsage(date.AddDays(3), 13, 18), new StoredUsage("category1", date.AddDays(3), 13, 18),
new StoredUsage(date.AddDays(5), 15, 20), new StoredUsage("category1", date.AddDays(5), 15, 20),
new StoredUsage(date.AddDays(7), 17, 22) new StoredUsage("category1", date.AddDays(7), 17, 22)
}; };
A.CallTo(() => usageStore.QueryAsync("key", new DateTime(2016, 1, 1), new DateTime(2016, 1, 31))) A.CallTo(() => usageStore.QueryAsync("MyKey1", new DateTime(2016, 1, 1), new DateTime(2016, 1, 31)))
.Returns(originalData); .Returns(originalData);
var result = await sut.GetMonthlyCallsAsync("key", date); var result = await sut.GetMonthlyCallsAsync("MyKey1", date);
Assert.Equal(55, result); Assert.Equal(55, result);
} }
@ -75,45 +74,82 @@ namespace Squidex.Infrastructure.UsageTracking
[Fact] [Fact]
public async Task Should_fill_missing_days() public async Task Should_fill_missing_days()
{ {
var dateFrom = DateTime.Today; var f = DateTime.Today;
var dateTo = DateTime.Today.AddDays(7); var t = DateTime.Today.AddDays(4);
IReadOnlyList<StoredUsage> originalData = new List<StoredUsage> var originalData = new List<StoredUsage>
{ {
new StoredUsage(dateFrom.AddDays(1), 10, 15), new StoredUsage("MyCategory1", f.AddDays(1), 10, 15),
new StoredUsage(dateFrom.AddDays(3), 13, 18), new StoredUsage("MyCategory1", f.AddDays(3), 13, 18),
new StoredUsage(dateFrom.AddDays(5), 15, 20), new StoredUsage("MyCategory1", f.AddDays(4), 15, 20),
new StoredUsage(dateFrom.AddDays(7), 17, 22) new StoredUsage(null, f.AddDays(0), 17, 22),
new StoredUsage(null, f.AddDays(2), 11, 14),
}; };
A.CallTo(() => usageStore.QueryAsync("key", dateFrom, dateTo)) A.CallTo(() => usageStore.QueryAsync("MyKey1", f, t))
.Returns(originalData); .Returns(originalData);
var result = await sut.QueryAsync("key", dateFrom, dateTo); var result = await sut.QueryAsync("MyKey1", f, t);
result.Should().BeEquivalentTo(new List<StoredUsage> var expected = new Dictionary<string, List<DateUsage>>
{ {
new StoredUsage(dateFrom.AddDays(0), 00, 00), ["MyCategory1"] = new List<DateUsage>
new StoredUsage(dateFrom.AddDays(1), 10, 15), {
new StoredUsage(dateFrom.AddDays(2), 00, 00), new DateUsage(f.AddDays(0), 00, 00),
new StoredUsage(dateFrom.AddDays(3), 13, 18), new DateUsage(f.AddDays(1), 10, 15),
new StoredUsage(dateFrom.AddDays(4), 00, 00), new DateUsage(f.AddDays(2), 00, 00),
new StoredUsage(dateFrom.AddDays(5), 15, 20), new DateUsage(f.AddDays(3), 13, 18),
new StoredUsage(dateFrom.AddDays(6), 00, 00), new DateUsage(f.AddDays(4), 15, 20),
new StoredUsage(dateFrom.AddDays(7), 17, 22) },
}); ["*"] = new List<DateUsage>
{
new DateUsage(f.AddDays(0), 17, 22),
new DateUsage(f.AddDays(1), 00, 00),
new DateUsage(f.AddDays(2), 11, 14),
new DateUsage(f.AddDays(3), 00, 00),
new DateUsage(f.AddDays(4), 00, 00),
}
};
result.Should().BeEquivalentTo(expected);
}
[Fact]
public async Task Should_fill_missing_days_with_star()
{
var f = DateTime.Today;
var t = DateTime.Today.AddDays(4);
A.CallTo(() => usageStore.QueryAsync("MyKey1", f, t))
.Returns(new List<StoredUsage>());
var result = await sut.QueryAsync("MyKey1", f, t);
var expected = new Dictionary<string, List<DateUsage>>
{
["*"] = new List<DateUsage>
{
new DateUsage(f.AddDays(0), 00, 00),
new DateUsage(f.AddDays(1), 00, 00),
new DateUsage(f.AddDays(2), 00, 00),
new DateUsage(f.AddDays(3), 00, 00),
new DateUsage(f.AddDays(4), 00, 00),
}
};
result.Should().BeEquivalentTo(expected);
} }
[Fact] [Fact]
public async Task Should_not_track_if_weight_less_than_zero() public async Task Should_not_track_if_weight_less_than_zero()
{ {
await sut.TrackAsync("key1", -1, 1000); await sut.TrackAsync("MyKey1", "MyCategory", -1, 1000);
await sut.TrackAsync("key1", 0, 1000); await sut.TrackAsync("MyKey1", "MyCategory", 0, 1000);
sut.Next(); sut.Next();
sut.Dispose(); sut.Dispose();
A.CallTo(() => usageStore.TrackUsagesAsync(A<DateTime>.Ignored, A<string>.Ignored, A<double>.Ignored, A<double>.Ignored)).MustNotHaveHappened(); A.CallTo(() => usageStore.TrackUsagesAsync(A<DateTime>.Ignored, A<string>.Ignored, A<string>.Ignored, A<double>.Ignored, A<double>.Ignored)).MustNotHaveHappened();
} }
[Fact] [Fact]
@ -121,30 +157,24 @@ namespace Squidex.Infrastructure.UsageTracking
{ {
var today = DateTime.Today; var today = DateTime.Today;
A.CallTo(() => usageStore.TrackUsagesAsync(today, "key1", 1.0, 1000)) await sut.TrackAsync("MyKey1", "MyCategory1", 1, 1000);
.Returns(TaskHelper.Done);
A.CallTo(() => usageStore.TrackUsagesAsync(today, "key2", 1.5, 5000)) await sut.TrackAsync("MyKey2", "MyCategory1", 1.0, 2000);
.Returns(TaskHelper.Done); await sut.TrackAsync("MyKey2", "MyCategory1", 0.5, 3000);
A.CallTo(() => usageStore.TrackUsagesAsync(today, "key3", 0.9, 15000)) await sut.TrackAsync("MyKey3", "MyCategory1", 0.3, 4000);
.Returns(TaskHelper.Done); await sut.TrackAsync("MyKey3", "MyCategory1", 0.1, 5000);
await sut.TrackAsync("MyKey3", null, 0.5, 2000);
await sut.TrackAsync("key1", 1, 1000); await sut.TrackAsync("MyKey3", null, 0.5, 6000);
await sut.TrackAsync("key2", 1.0, 2000);
await sut.TrackAsync("key2", 0.5, 3000);
await sut.TrackAsync("key3", 0.3, 4000);
await sut.TrackAsync("key3", 0.1, 5000);
await sut.TrackAsync("key3", 0.5, 6000);
sut.Next(); sut.Next();
sut.Dispose(); sut.Dispose();
A.CallTo(() => usageStore.TrackUsagesAsync(today, "key1", 1.0, 1000)).MustHaveHappened(); A.CallTo(() => usageStore.TrackUsagesAsync(today, "MyKey1", "MyCategory1", 1.0, 1000)).MustHaveHappened();
A.CallTo(() => usageStore.TrackUsagesAsync(today, "key2", 1.5, 5000)).MustHaveHappened(); A.CallTo(() => usageStore.TrackUsagesAsync(today, "MyKey2", "MyCategory1", 1.5, 5000)).MustHaveHappened();
A.CallTo(() => usageStore.TrackUsagesAsync(today, "key3", 0.9, 15000)).MustHaveHappened(); A.CallTo(() => usageStore.TrackUsagesAsync(today, "MyKey3", "MyCategory1", 0.4, 9000)).MustHaveHappened();
A.CallTo(() => usageStore.TrackUsagesAsync(today, "MyKey3", "*", 1.0, 8000)).MustHaveHappened();
} }
} }
} }

4
tests/Squidex.Infrastructure.Tests/UsageTracking/ThreadingUsageTrackerTests.cs

@ -28,9 +28,9 @@ namespace Squidex.Infrastructure.UsageTracking
[Fact] [Fact]
public async Task Should_forward_track_call() public async Task Should_forward_track_call()
{ {
await sut.TrackAsync("MyKey", 123, 456); await sut.TrackAsync("MyKey", "MyCategory", 123, 456);
A.CallTo(() => inner.TrackAsync("MyKey", 123, 456)) A.CallTo(() => inner.TrackAsync("MyKey", "MyCategory", 123, 456))
.MustHaveHappened(); .MustHaveHappened();
} }

10
tests/Squidex.Tests/Pipeline/ApiCostsFilterTests.cs

@ -86,7 +86,7 @@ namespace Squidex.Tests.Pipeline
Assert.Equal(429, (actionContext.Result as StatusCodeResult).StatusCode); Assert.Equal(429, (actionContext.Result as StatusCodeResult).StatusCode);
Assert.False(isNextCalled); Assert.False(isNextCalled);
A.CallTo(() => usageTracker.TrackAsync(A<string>.Ignored, A<double>.Ignored, A<double>.Ignored)) A.CallTo(() => usageTracker.TrackAsync(A<string>.Ignored, A<string>.Ignored, A<double>.Ignored, A<double>.Ignored))
.MustNotHaveHappened(); .MustNotHaveHappened();
} }
@ -104,7 +104,7 @@ namespace Squidex.Tests.Pipeline
Assert.True(isNextCalled); Assert.True(isNextCalled);
A.CallTo(() => usageTracker.TrackAsync(A<string>.Ignored, 13, A<double>.Ignored)) A.CallTo(() => usageTracker.TrackAsync(A<string>.Ignored, A<string>.Ignored, 13, A<double>.Ignored))
.MustHaveHappened(); .MustHaveHappened();
} }
@ -122,7 +122,7 @@ namespace Squidex.Tests.Pipeline
Assert.True(isNextCalled); Assert.True(isNextCalled);
A.CallTo(() => usageTracker.TrackAsync(A<string>.Ignored, 13, A<double>.Ignored)) A.CallTo(() => usageTracker.TrackAsync(A<string>.Ignored, A<string>.Ignored, 13, A<double>.Ignored))
.MustHaveHappened(); .MustHaveHappened();
} }
@ -140,7 +140,7 @@ namespace Squidex.Tests.Pipeline
Assert.True(isNextCalled); Assert.True(isNextCalled);
A.CallTo(() => usageTracker.TrackAsync(A<string>.Ignored, A<double>.Ignored, A<double>.Ignored)) A.CallTo(() => usageTracker.TrackAsync(A<string>.Ignored, A<string>.Ignored, A<double>.Ignored, A<double>.Ignored))
.MustNotHaveHappened(); .MustNotHaveHappened();
} }
@ -156,7 +156,7 @@ namespace Squidex.Tests.Pipeline
Assert.True(isNextCalled); Assert.True(isNextCalled);
A.CallTo(() => usageTracker.TrackAsync(A<string>.Ignored, A<double>.Ignored, A<double>.Ignored)) A.CallTo(() => usageTracker.TrackAsync(A<string>.Ignored, A<string>.Ignored, A<double>.Ignored, A<double>.Ignored))
.MustNotHaveHappened(); .MustNotHaveHappened();
} }

Loading…
Cancel
Save