Skip to content

Commit

Permalink
fix(backend): 添加AcHistory的部分测试和修复
Browse files Browse the repository at this point in the history
有些测试没有通过,因为InMemoryDB目前还不支持外键相关的Cascade删除(见https://github.com/dotnet/efcore/issues/3924)。考虑在代码里显式删除相关实体,或者使用真实数据库进行测试。
  • Loading branch information
Liu233w authored and mergify[bot] committed Apr 15, 2020
1 parent 498d5ee commit 4484d4f
Show file tree
Hide file tree
Showing 5 changed files with 340 additions and 8 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ public async Task SaveOrReplaceAcHistory(SaveOrReplaceAcHistoryInput input)
.Where(e => e.UserId == AbpSession.UserId.Value)
.OrderBy(e => e.CreationTime)
.FirstOrDefaultAsync();
if (latestItem.CreationTime.Date == Clock.Now.Date)
if (latestItem != null && latestItem.CreationTime.Date == Clock.Now.Date)
{
await _acHistoryRepository.DeleteAsync(latestItem);
}
Expand All @@ -56,11 +56,14 @@ public async Task DeleteAcHistory(DeleteAcHistoryInput input)
await _acHistoryRepository.DeleteAsync(entity);
}

foreach (var id in input.Ids)
if (input.Ids != null)
{
var entity = await GetAuthorizedEntity(id);
// 关联的 AcWorkerHistory 会自动删除
await _acHistoryRepository.DeleteAsync(entity);
foreach (var id in input.Ids)
{
var entity = await GetAuthorizedEntity(id);
// 关联的 AcWorkerHistory 会自动删除
await _acHistoryRepository.DeleteAsync(entity);
}
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,17 @@
using System.ComponentModel.DataAnnotations;
using System.Diagnostics.CodeAnalysis;
using Abp.AutoMapper;
using Abp.Runtime.Validation;

namespace AcmStatisticsBackend.Crawlers.Dto
{
[AutoMap(typeof(AcWorkerHistory))]
public class AcWorkerHistoryDto
public class AcWorkerHistoryDto : ICustomValidate
{
/// <summary>
/// 爬虫名称。前端可以根据元数据从此名称获取爬虫标题。
/// </summary>
[Required]
public string CrawlerName { get; set; }

/// <summary>
Expand Down Expand Up @@ -44,7 +49,14 @@ public class AcWorkerHistoryDto
///
/// 如果 <see cref="HasSolvedList"/> 为false,返回一个空的列表。
/// </summary>
[Required]
public string[] SolvedList { get; set; }

public void AddValidationErrors(CustomValidationContext context)
{
if (ErrorMessage == null && HasSolvedList && SolvedList == null)
{
context.Results.Add(new ValidationResult("当 HasSolvedList 为 true 时,SolvedList 不能为 null"));
}
}
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
namespace AcmStatisticsBackend.Crawlers.Dto
using System.Diagnostics.CodeAnalysis;

namespace AcmStatisticsBackend.Crawlers.Dto
{
public class DeleteAcHistoryInput
{
Expand All @@ -10,6 +12,7 @@ public class DeleteAcHistoryInput
/// <summary>
/// 删除一系列历史记录
/// </summary>
[MaybeNull]
public long[] Ids { get; set; }
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
using System;

namespace AcmStatisticsBackend
{
public static class AcmStatisticsBackendExtensions
{
#pragma warning disable SA1618
/// <summary>
/// 用法:
/// <code>
/// Get().A().Object().WithIn(it => {
/// it.methodA();
/// it.methodB();
/// })
/// </code>
/// </summary>
public static TR WithIn<TT, TR>(this TT obj, Func<TT, TR> func)
where TT : class
{
return func(obj);
}
#pragma warning restore SA1618

public static TR WithIn<TT, TR>(this ref TT obj, Func<TT, TR> func)
where TT : struct
{
return func(obj);
}

public static void WithIn<T>(this T obj, Action<T> action)
where T : class
{
action(obj);
}

public static void WithIn<T>(this ref T obj, Action<T> action)
where T : struct
{
action(obj);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,272 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Threading.Tasks;
using Abp.Timing;
using AcmStatisticsBackend.Crawlers;
using AcmStatisticsBackend.Crawlers.Dto;
using Microsoft.EntityFrameworkCore;
using Shouldly;
using Xunit;

namespace AcmStatisticsBackend.Tests.Crawlers
{
public class AcHistoryAppService_Tests : AcmStatisticsBackendTestBase
{
private readonly IAcHistoryAppService _acHistoryAppService;

private readonly TestClockProvider _testClockProvider;

public AcHistoryAppService_Tests()
{
_acHistoryAppService = Resolve<AcHistoryAppService>();
_testClockProvider = new TestClockProvider();
Clock.Provider = _testClockProvider;
}

[Fact]
public async Task SaveOrReplaceAcHistory_能够存储记录()
{
// arrange
LoginAsDefaultTenantAdmin();
_testClockProvider.Now = new DateTime(2020, 4, 1, 10, 0, 0);

// act
await _acHistoryAppService.SaveOrReplaceAcHistory(new SaveOrReplaceAcHistoryInput
{
Solved = 3,
Submission = 20,
MainUsername = "mainUser",
AcWorkerHistories = new List<AcWorkerHistoryDto>
{
new AcWorkerHistoryDto
{
Username = "u1",
CrawlerName = "c1",
Solved = 3,
Submission = 20,
ErrorMessage = null,
HasSolvedList = true,
SolvedList = new string[]
{
"p1",
"p2",
"p3",
},
},
new AcWorkerHistoryDto
{
Username = "u2",
CrawlerName = "c2",
ErrorMessage = "Cannot find username",
},
},
});

// assert
await UsingDbContextAsync(async ctx =>
{
(await ctx.AcHistories.CountAsync()).ShouldBe(1);
var acHistory = await ctx.AcHistories.FirstAsync();
acHistory.Solved.ShouldBe(3);
acHistory.Submission.ShouldBe(20);
Debug.Assert(AbpSession.UserId != null, "AbpSession.UserId != null");
acHistory.UserId.ShouldBe(AbpSession.UserId.Value);
acHistory.CreationTime.ShouldBe(new DateTime(2020, 4, 1, 10, 0, 0));
acHistory.MainUsername.ShouldBe("mainUser");

(await ctx.AcWorkerHistories.CountAsync()).ShouldBe(2);
var list = await ctx.AcWorkerHistories.ToListAsync();
list[0].WithIn(it =>
{
it.Solved.ShouldBe(3);
it.Submission.ShouldBe(20);
it.Username.ShouldBe("u1");
it.CrawlerName.ShouldBe("c1");
it.AcHistoryId.ShouldBe(acHistory.Id);
it.ErrorMessage.ShouldBe(null);
it.HasSolvedList.ShouldBe(true);
it.SolvedList.ShouldBe(new string[]
{
"p1",
"p2",
"p3",
});
});
list[1].WithIn(it =>
{
it.Solved.ShouldBe(0);
it.HasSolvedList.ShouldBe(false);
it.SolvedList.ShouldBe(new string[0]);
it.ErrorMessage.ShouldBe("Cannot find username");
});
});
}

[Fact]
public async Task SaveOrReplaceAcHistory_能够顶掉同一天的记录()
{
// arrange
LoginAsDefaultTenantAdmin();

// act
_testClockProvider.Now = new DateTime(2020, 4, 1, 10, 0, 0);
await _acHistoryAppService.SaveOrReplaceAcHistory(new SaveOrReplaceAcHistoryInput
{
Solved = 1,
Submission = 10,
MainUsername = "u1",
AcWorkerHistories = new List<AcWorkerHistoryDto>
{
new AcWorkerHistoryDto
{
CrawlerName = "c1",
Username = "u1",
Solved = 1,
Submission = 10,
HasSolvedList = false,
},
},
});

// 第二次存储
_testClockProvider.Now = new DateTime(2020, 4, 1, 20, 0, 0);
await _acHistoryAppService.SaveOrReplaceAcHistory(new SaveOrReplaceAcHistoryInput
{
Solved = 5,
Submission = 10,
MainUsername = "u1",
AcWorkerHistories = new List<AcWorkerHistoryDto>
{
new AcWorkerHistoryDto
{
CrawlerName = "c1",
Username = "u1",
Solved = 5,
Submission = 10,
HasSolvedList = false,
},
},
});

// assert
await UsingDbContextAsync(async ctx =>
{
(await ctx.AcHistories.CountAsync()).ShouldBe(1);
var history = await ctx.AcHistories.FirstAsync();
history.Solved.ShouldBe(5);

(await ctx.AcWorkerHistories.CountAsync()).ShouldBe(1);
var workerHistory = await ctx.AcWorkerHistories.FirstAsync();
workerHistory.AcHistoryId.ShouldBe(history.Id);
workerHistory.Solved.ShouldBe(5);
});
}

[Fact]
public async Task SaveOrReplaceAcHistory_能够保留不同天的记录()
{
// arrange
LoginAsDefaultTenantAdmin();

// act
_testClockProvider.Now = new DateTime(2020, 4, 1, 10, 0, 0);
await _acHistoryAppService.SaveOrReplaceAcHistory(new SaveOrReplaceAcHistoryInput
{
Solved = 1,
Submission = 10,
MainUsername = "u1",
AcWorkerHistories = new List<AcWorkerHistoryDto>
{
new AcWorkerHistoryDto
{
CrawlerName = "c1",
Username = "u1",
Solved = 1,
Submission = 10,
HasSolvedList = false,
},
},
});

// 第二次存储
_testClockProvider.Now = new DateTime(2020, 4, 2, 10, 0, 0);
await _acHistoryAppService.SaveOrReplaceAcHistory(new SaveOrReplaceAcHistoryInput
{
Solved = 5,
Submission = 10,
MainUsername = "u1",
AcWorkerHistories = new List<AcWorkerHistoryDto>
{
new AcWorkerHistoryDto
{
CrawlerName = "c1",
Username = "u1",
Solved = 5,
Submission = 10,
HasSolvedList = false,
},
},
});

// assert
await UsingDbContextAsync(async ctx =>
{
(await ctx.AcHistories.CountAsync()).ShouldBe(2);
(await ctx.AcWorkerHistories.CountAsync()).ShouldBe(2);
});
}

[Fact]
public async Task DeleteAcHistory_能够移除AcHistory和AcWorkerHistory()
{
// arrange
LoginAsDefaultTenantAdmin();
_testClockProvider.Now = new DateTime(2020, 4, 1, 10, 0, 0);
await _acHistoryAppService.SaveOrReplaceAcHistory(new SaveOrReplaceAcHistoryInput
{
Solved = 1,
Submission = 10,
MainUsername = "u1",
AcWorkerHistories = new List<AcWorkerHistoryDto>
{
new AcWorkerHistoryDto
{
CrawlerName = "c1",
Username = "u1",
Solved = 1,
Submission = 10,
HasSolvedList = false,
},
},
});

// act
await _acHistoryAppService.DeleteAcHistory(new DeleteAcHistoryInput
{
Id = 1,
});

// assert
await UsingDbContextAsync(async ctx =>
{
(await ctx.AcHistories.CountAsync()).ShouldBe(0);
(await ctx.AcWorkerHistories.CountAsync()).ShouldBe(0);
});
}
}

internal class TestClockProvider : IClockProvider
{
public DateTime Normalize(DateTime dateTime)
{
return ClockProviders.Utc.Normalize(dateTime);
}

public DateTime Now { get; set; }

public DateTimeKind Kind => DateTimeKind.Utc;

public bool SupportsMultipleTimezone => true;
}
}

0 comments on commit 4484d4f

Please sign in to comment.