444 lines
17 KiB
C#
444 lines
17 KiB
C#
using DD.Persistence.Client;
|
||
using DD.Persistence.Client.Clients;
|
||
using DD.Persistence.Client.Clients.Interfaces;
|
||
using DD.Persistence.Client.Clients.Interfaces.Refit;
|
||
using DD.Persistence.Database.Entity;
|
||
using DD.Persistence.Models.ChangeLog;
|
||
using DD.Persistence.Models.Requests;
|
||
using Mapster;
|
||
using Microsoft.EntityFrameworkCore;
|
||
using Microsoft.Extensions.DependencyInjection;
|
||
using Microsoft.Extensions.Logging;
|
||
using UuidExtensions;
|
||
using Xunit;
|
||
using static System.Runtime.InteropServices.JavaScript.JSType;
|
||
|
||
namespace DD.Persistence.IntegrationTests.Controllers;
|
||
public class ChangeLogControllerTest : BaseIntegrationTest
|
||
{
|
||
private readonly IChangeLogClient client;
|
||
private readonly PaginationRequest paginationRequest;
|
||
private static readonly Random random = new();
|
||
|
||
public ChangeLogControllerTest(WebAppFactoryFixture factory) : base(factory)
|
||
{
|
||
var refitClientFactory = scope.ServiceProvider
|
||
.GetRequiredService<IRefitClientFactory<IRefitChangeLogClient>>();
|
||
var logger = scope.ServiceProvider.GetRequiredService<ILogger<ChangeLogClient>>();
|
||
|
||
client = scope.ServiceProvider
|
||
.GetRequiredService<IChangeLogClient>();
|
||
|
||
paginationRequest = new PaginationRequest()
|
||
{
|
||
Skip = 0,
|
||
Take = 10,
|
||
SortSettings = string.Empty,
|
||
};
|
||
}
|
||
|
||
[Fact]
|
||
public async Task ClearAndInsertRange()
|
||
{
|
||
// arrange
|
||
var insertedCount = 10;
|
||
var idDiscriminator = Guid.NewGuid();
|
||
var otherOldDtos = GenerateChangeLogCreateRequest(10);
|
||
await client.AddRange(Guid.NewGuid(), otherOldDtos, Guid.NewGuid().ToString(), CancellationToken.None);
|
||
|
||
var oldDtos = GenerateChangeLogCreateRequest(10);
|
||
await client.AddRange(idDiscriminator, oldDtos, Guid.NewGuid().ToString(), CancellationToken.None);
|
||
|
||
var newDtos = GenerateChangeLogCreateRequest(insertedCount);
|
||
|
||
//act
|
||
var result = await client.ClearAndAddRange(idDiscriminator, newDtos, "Добавление новых элементов и очистка старых", CancellationToken.None);
|
||
|
||
// assert
|
||
Assert.Equal(insertedCount * 2, result);
|
||
var data = await client.GetByDate(idDiscriminator, DateTimeOffset.Now.AddSeconds(1), new PaginationRequest { Take = 10 * insertedCount }, CancellationToken.None);
|
||
Assert.Equal(insertedCount, data.Count);
|
||
}
|
||
|
||
[Fact]
|
||
public async Task AddRange_returns_success()
|
||
{
|
||
// arrange
|
||
var count = 3;
|
||
var idDiscriminator = Guid.NewGuid();
|
||
var dtos = GenerateChangeLogCreateRequest(count);
|
||
var comment = "Создаю 3 элемента";
|
||
|
||
// act
|
||
var result = await client.AddRange(idDiscriminator, dtos, comment, CancellationToken.None);
|
||
|
||
// assert
|
||
Assert.Equal(count, result);
|
||
}
|
||
|
||
[Fact]
|
||
public async Task Update_returns_success()
|
||
{
|
||
//arrange
|
||
var begin = DateTimeOffset.UtcNow;
|
||
var idDiscriminator = Guid.NewGuid();
|
||
var dtos = GenerateChangeLogCreateRequest(1);
|
||
var original = dtos.First();
|
||
var comment = "Создаю 1 элемент";
|
||
var result = await client.AddRange(idDiscriminator, [original], comment, CancellationToken.None);
|
||
|
||
var query = dbContext.Set<ChangeLog>()
|
||
.Include(x => x.CreatedCommit)
|
||
.Where(x => x.CreatedCommit.DiscriminatorId == idDiscriminator);
|
||
var entity = await query.FirstAsync();
|
||
var modified = entity.Adapt<ChangeLogBaseDto>();
|
||
var key = modified.Value.First().Key;
|
||
modified.Value[key] = random.NextDouble();
|
||
|
||
// act
|
||
comment = "Обновляю 1 элемент";
|
||
result = await client.UpdateRange(idDiscriminator, [modified], comment, CancellationToken.None);
|
||
|
||
// assert
|
||
Assert.Equal(2, result);
|
||
|
||
var now = DateTimeOffset.UtcNow.AddSeconds(1);
|
||
|
||
var changeLogResult = await client.GetChangeLogForInterval(idDiscriminator, begin, now, new CancellationToken());
|
||
Assert.NotNull(changeLogResult);
|
||
|
||
var obsoleteDto = changeLogResult
|
||
.Where(e => e.Obsolete.HasValue)
|
||
.First();
|
||
|
||
var activeDto = changeLogResult
|
||
.Where(e => !e.Obsolete.HasValue)
|
||
.OrderByDescending(e => e.Creation)
|
||
.First();
|
||
|
||
Assert.Equal(activeDto.Id, obsoleteDto.IdNext);
|
||
|
||
}
|
||
|
||
[Fact]
|
||
public async Task UpdateRange_returns_success()
|
||
{
|
||
var count = 2;
|
||
var idDiscriminator = Guid.NewGuid();
|
||
var dtos = GenerateChangeLogCreateRequest(count);
|
||
var comment = "Создаю 3 элемента";
|
||
|
||
// act
|
||
var result = await client.AddRange(idDiscriminator, dtos, comment, CancellationToken.None);
|
||
var paginatedResult = await client.GetByDate(idDiscriminator, DateTimeOffset.UtcNow.AddDays(1), paginationRequest, CancellationToken.None);
|
||
// act
|
||
comment = "Обновляю 3 элемента";
|
||
result = await client.UpdateRange(idDiscriminator, paginatedResult.Items, comment, CancellationToken.None);
|
||
|
||
// assert
|
||
Assert.Equal(count * 2, result);
|
||
}
|
||
|
||
[Fact]
|
||
public async Task DeleteRange_returns_success()
|
||
{
|
||
// arrange
|
||
var insertedCount = 10;
|
||
var idDiscriminator = Guid.NewGuid();
|
||
var otherOldDtos = GenerateChangeLogCreateRequest(10);
|
||
await client.AddRange(Guid.NewGuid(), otherOldDtos, Guid.NewGuid().ToString(), CancellationToken.None);
|
||
|
||
var oldDtos = GenerateChangeLogCreateRequest(10);
|
||
await client.AddRange(idDiscriminator, oldDtos, Guid.NewGuid().ToString(), CancellationToken.None);
|
||
var existing = await client.GetByDate(idDiscriminator, DateTimeOffset.UtcNow.AddSeconds(1), new PaginationRequest { Take= 100*insertedCount}, CancellationToken.None);
|
||
var ids = existing.Items.Select(e => e.Id);
|
||
|
||
// act
|
||
var result = await client.DeleteRange(idDiscriminator, ids, "Удаление нескольких записей", CancellationToken.None);
|
||
|
||
// assert
|
||
Assert.Equal(insertedCount, result);
|
||
var items = await dbContext.Set<ChangeLog>()
|
||
.Where(e=> ids.Contains(e.Id))
|
||
.ToArrayAsync(CancellationToken.None);
|
||
|
||
Assert.Equal(insertedCount, items.Count());
|
||
Assert.All(items, i => Assert.NotNull(i.IdObsoletedCommit));
|
||
}
|
||
|
||
static ChangeLogCommit GenerateCommit(Guid discriminatorId, DateTimeOffset creation)
|
||
=> GenerateCommit(discriminatorId, Guid.NewGuid(), creation);
|
||
|
||
static ChangeLogCommit GenerateCommit(Guid discriminatorId, Guid userId, DateTimeOffset creation)
|
||
=> new ChangeLogCommit
|
||
{
|
||
Id = Guid.NewGuid(),
|
||
DiscriminatorId = discriminatorId,
|
||
IdAuthor = userId,
|
||
Creation = creation,
|
||
Comment = Guid.NewGuid().ToString(),
|
||
};
|
||
|
||
static IEnumerable<ChangeLog> GenerateChangeLog(int count, ChangeLogCommit createCommit)
|
||
=> GenerateChangeLog(count, createCommit, null, null);
|
||
|
||
static IEnumerable<ChangeLog> GenerateChangeLog(int count, ChangeLogCommit createCommit, ChangeLogCommit? obsoleteCommit, Guid? idNext)
|
||
{
|
||
for (var i = 0; i< count; i++)
|
||
{
|
||
yield return new ChangeLog
|
||
{
|
||
Id = Guid.NewGuid(),
|
||
CreatedCommit = createCommit,
|
||
IdCreatedCommit = createCommit.Id,
|
||
ObsoletedCommit = obsoleteCommit,
|
||
IdObsoletedCommit = obsoleteCommit?.Id,
|
||
IdNext = idNext,
|
||
Value = new Dictionary<string, object> { { "Guid.NewGuid()", random.NextDouble() } }
|
||
};
|
||
}
|
||
}
|
||
|
||
static ChangeLogCommit GenerateCommitWithNewData(Guid discriminatorId, DateTimeOffset creation, int count)
|
||
{
|
||
var commit = GenerateCommit(discriminatorId, creation);
|
||
commit.ChangeLogCreatedItems = GenerateChangeLog(count, commit).ToList();
|
||
return commit;
|
||
}
|
||
|
||
async Task<ChangeLogCommit> SeedCommitWithNewData(Guid discriminatorId, DateTimeOffset creation, int count)
|
||
{
|
||
var data = GenerateCommitWithNewData(discriminatorId, creation, count);
|
||
dbContext.Set<ChangeLogCommit>().Add(data);
|
||
await dbContext.SaveChangesAsync();
|
||
return data;
|
||
}
|
||
|
||
[Fact]
|
||
public async Task GetDatesRange_returns_success()
|
||
{
|
||
//arrange
|
||
var dateMin = DateTimeOffset.UtcNow.AddDays(-1);
|
||
var dateMax = DateTimeOffset.UtcNow;
|
||
var dateMid = dateMin + 0.5 * (dateMax - dateMin);
|
||
var idDiscriminator = Guid.NewGuid();
|
||
var idOtherDiscriminator = Guid.NewGuid();
|
||
var count = 15;
|
||
|
||
await SeedCommitWithNewData(idDiscriminator, dateMin, count);
|
||
await SeedCommitWithNewData(idDiscriminator, dateMid, count);
|
||
await SeedCommitWithNewData(idDiscriminator, dateMax, count);
|
||
await SeedCommitWithNewData(idOtherDiscriminator, dateMin.AddDays(-1), count);
|
||
|
||
// act
|
||
var result = await client.GetDatesRange(idDiscriminator, CancellationToken.None);
|
||
|
||
// assert
|
||
Assert.NotNull(result);
|
||
Assert.Equal(dateMin, result.From, TimeSpan.FromSeconds(1));
|
||
Assert.Equal(dateMax, result.To, TimeSpan.FromSeconds(1));
|
||
}
|
||
|
||
[Fact]
|
||
public async Task GetByDate_returns_success()
|
||
{
|
||
// arrange
|
||
var dbSetCommit = dbContext.Set<ChangeLogCommit>();
|
||
var dbSet = dbContext.Set<ChangeLog>();
|
||
|
||
var idDiscriminator = Guid.NewGuid();
|
||
var date = DateTimeOffset.UtcNow;
|
||
var expected = 10;
|
||
var skip = 2;
|
||
var take = skip + 2;
|
||
var count = expected + take;
|
||
var commit = GenerateCommit(idDiscriminator, date);
|
||
dbSetCommit.Add(commit);
|
||
var commitData = GenerateChangeLog(count, commit);
|
||
dbSet.AddRange(commitData);
|
||
|
||
await dbContext.SaveChangesAsync(CancellationToken.None);
|
||
var dataWithIds = await dbSet
|
||
.Where(e=>e.CreatedCommit.DiscriminatorId == idDiscriminator)
|
||
.AsNoTracking()
|
||
.ToArrayAsync(CancellationToken.None);
|
||
|
||
var itemsToDelete = dataWithIds.Skip(skip).Take(take);
|
||
var idsToDelete = itemsToDelete.Select(x => x.Id).ToArray();
|
||
|
||
// act
|
||
var deletedCount = await client.DeleteRange(idDiscriminator, idsToDelete, "Удаление нескольких записей", CancellationToken.None);
|
||
|
||
// assert
|
||
var moment = DateTimeOffset.UtcNow.AddSeconds(1);
|
||
var result = await client.GetByDate(idDiscriminator, moment, new() { Take = count *100}, new CancellationToken());
|
||
|
||
Assert.NotNull(result);
|
||
Assert.Equal(expected, result.Items.Count());
|
||
}
|
||
|
||
[Fact]
|
||
public async Task GetChangeLogForInterval_returns_success()
|
||
{
|
||
// arrange
|
||
var dateMin = DateTimeOffset.UtcNow.AddDays(- 11);
|
||
var dateMax = DateTimeOffset.UtcNow;
|
||
var dateMid = dateMin + 0.5 * (dateMax - dateMin);
|
||
var idDiscriminator = Guid.NewGuid();
|
||
var idOtherDiscriminator = Guid.NewGuid();
|
||
var count = 17;
|
||
|
||
var commitMin = await SeedCommitWithNewData(idDiscriminator, dateMin, count);
|
||
var commitMid = await SeedCommitWithNewData(idDiscriminator, dateMid, count);
|
||
await SeedCommitWithNewData(idDiscriminator, dateMax, count);
|
||
await SeedCommitWithNewData(idOtherDiscriminator, dateMin.AddDays(-1), count);
|
||
|
||
var dateBegin = commitMin.Creation;
|
||
var dateEnd = commitMid.Creation.AddSeconds(1);
|
||
|
||
var expectedCount = commitMin.ChangeLogCreatedItems.Count()
|
||
+ commitMid.ChangeLogCreatedItems.Count();
|
||
//act
|
||
|
||
var result = await client.GetChangeLogForInterval(idDiscriminator, dateBegin, dateEnd, CancellationToken.None);
|
||
|
||
//assert
|
||
Assert.Equal(expectedCount, result.Count());
|
||
}
|
||
|
||
[Fact]
|
||
public async Task GetStatistics_returns_success()
|
||
{
|
||
// arrange
|
||
var discriminatorId = Guid.NewGuid();
|
||
// создаем записи
|
||
var date0 = DateTimeOffset.UtcNow.AddDays(-17);
|
||
var day0 = new DateOnly(date0.Year, date0.Month, date0.Day);
|
||
var inserted0 = 17;
|
||
var commit0 = await SeedCommitWithNewData(discriminatorId, date0, inserted0);
|
||
|
||
// создаем еще записи
|
||
var date1 = DateTimeOffset.UtcNow.AddDays(-7);
|
||
var day1 = new DateOnly(date1.Year, date1.Month, date1.Day);
|
||
var inserted1 = inserted0 + 5;
|
||
var commit1 = await SeedCommitWithNewData(discriminatorId, date1, inserted1);
|
||
|
||
// создаем еще записи с новым дискриминатором
|
||
await SeedCommitWithNewData(Guid.NewGuid(), date1, 17);
|
||
|
||
// обновим
|
||
var date2 = DateTimeOffset.UtcNow;
|
||
var day2 = new DateOnly(date2.Year, date2.Month, date2.Day);
|
||
var updated = commit0.ChangeLogCreatedItems.Select(i => new ChangeLogBaseDto { Id = i.Id, Value = i.Value });
|
||
await client.UpdateRange(commit0.DiscriminatorId, updated, Guid.NewGuid().ToString(), CancellationToken.None);
|
||
|
||
// удалим из коммита
|
||
var idToDelete = commit1.ChangeLogCreatedItems.First().Id;
|
||
await client.DeleteRange(commit1.DiscriminatorId, [idToDelete], Guid.NewGuid().ToString(), CancellationToken.None);
|
||
|
||
//act
|
||
var request = new ChangeLogQuery()
|
||
{
|
||
DiscriminatorId = commit0.DiscriminatorId,
|
||
UserId = null
|
||
};
|
||
var statistics = await client.GetStatistics(request, CancellationToken.None);
|
||
|
||
//assert
|
||
Assert.Equal(3, statistics.Count());
|
||
|
||
var stat0 = statistics.First(s => s.Date == day0);
|
||
Assert.Equal(inserted0, stat0.CreatedChangeLogCount);
|
||
Assert.Equal(0, stat0.ObsoletedCount);
|
||
Assert.Equal(1, stat0.CommitCount);
|
||
|
||
var stat1 = statistics.First(s => s.Date == day1);
|
||
Assert.Equal(inserted1, stat1.CreatedChangeLogCount);
|
||
Assert.Equal(0, stat1.ObsoletedCount);
|
||
Assert.Equal(1, stat1.CommitCount);
|
||
|
||
var stat2 = statistics.First(s => s.Date == day2);
|
||
Assert.Equal(inserted0, stat2.CreatedChangeLogCount);
|
||
Assert.Equal(inserted0 + 1, stat2.ObsoletedCount);
|
||
Assert.Equal(2, stat2.CommitCount);
|
||
}
|
||
|
||
[Fact]
|
||
public async Task GetHistory_returns_success()
|
||
{
|
||
// arrange
|
||
var discriminatorId = Guid.NewGuid();
|
||
// создаем записи
|
||
var date0 = DateTimeOffset.UtcNow.AddDays(-17);
|
||
var inserted0 = 17;
|
||
var commit0 = await SeedCommitWithNewData(discriminatorId, date0, inserted0);
|
||
|
||
// обновим
|
||
var date2 = DateTimeOffset.UtcNow;
|
||
var updated = commit0.ChangeLogCreatedItems.Skip(1).Select(i => new ChangeLogBaseDto { Id = i.Id, Value = i.Value });
|
||
await client.UpdateRange(commit0.DiscriminatorId, updated, Guid.NewGuid().ToString(), CancellationToken.None);
|
||
|
||
// удалим из коммита
|
||
var idToDelete = commit0.ChangeLogCreatedItems.First().Id;
|
||
await client.DeleteRange(commit0.DiscriminatorId, [idToDelete], Guid.NewGuid().ToString(), CancellationToken.None);
|
||
|
||
var request = new ChangeLogQuery()
|
||
{
|
||
DiscriminatorId = commit0.DiscriminatorId,
|
||
UserId = null
|
||
};
|
||
var history = await client.GetHistory(request, CancellationToken.None);
|
||
Assert.Equal(3, history.Count());
|
||
|
||
var items = history.OrderBy(e => e.Creation);
|
||
var firstItem = items.First();
|
||
Assert.Equal(date0.DateTime.ToString(), firstItem.Creation.DateTime.ToString());
|
||
Assert.Equal(inserted0, firstItem.ChangeLogCreatedItems.Count());
|
||
Assert.Empty(firstItem.ChangeLogObsoletedItems);
|
||
|
||
var middleItem = items.Skip(1).Take(1).First();
|
||
Assert.Equal(date2.DateTime.ToString(), middleItem.Creation.DateTime.ToString());
|
||
Assert.Equal(updated.Count(), middleItem.ChangeLogObsoletedItems.Count());
|
||
Assert.Equal(updated.Count(), middleItem.ChangeLogCreatedItems.Count());
|
||
|
||
var lastItem = items.Last();
|
||
Assert.Equal(date2.DateTime.ToString(), lastItem.Creation.DateTime.ToString());
|
||
Assert.Single(lastItem.ChangeLogObsoletedItems);
|
||
|
||
//тест случая, когда данных за указанный период нет
|
||
request = new ChangeLogQuery()
|
||
{
|
||
DiscriminatorId = commit0.DiscriminatorId,
|
||
UserId = null,
|
||
GeDate = DateTimeOffset.UtcNow.AddMinutes(1),
|
||
LeDate = DateTimeOffset.UtcNow.AddDays(10),
|
||
};
|
||
history = await client.GetHistory(request, CancellationToken.None);
|
||
Assert.Empty(history);
|
||
|
||
//тест случая, когда данные за указанный период есть
|
||
request = new ChangeLogQuery()
|
||
{
|
||
DiscriminatorId = commit0.DiscriminatorId,
|
||
UserId = null,
|
||
GeDate = DateTimeOffset.UtcNow.AddDays(-17).AddMinutes(-10),
|
||
LeDate = DateTimeOffset.UtcNow.AddDays(-1),
|
||
};
|
||
history = await client.GetHistory(request, CancellationToken.None);
|
||
Assert.Single(history);
|
||
|
||
var createdChangeLogs = history.First().ChangeLogCreatedItems;
|
||
Assert.Equal(17, createdChangeLogs.Count());
|
||
}
|
||
|
||
private static IEnumerable<IDictionary<string, object>> GenerateChangeLogCreateRequest(int count)
|
||
{
|
||
for (int i = 0; i < count; i++)
|
||
yield return new Dictionary<string, object>()
|
||
{
|
||
{ "Key", random.NextDouble() }
|
||
};
|
||
|
||
}
|
||
}
|