IM/backend/IMTest/Service/AuthServiceTest.cs
2026-02-12 21:59:08 +08:00

209 lines
6.6 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using AutoMapper;
using IM_API.Dtos.Auth;
using IM_API.Dtos.User;
using IM_API.Exceptions;
using IM_API.Interface.Services;
using IM_API.Models;
using IM_API.Services;
using IM_API.Tools;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;
using Moq;
using System;
using System.Threading;
using System.Threading.Tasks;
using Xunit;
public class AuthServiceTests
{
// ---------- 辅助:创建独立的 InMemory DbContext ----------
private ImContext CreateDbContext()
{
var options = new DbContextOptionsBuilder<ImContext>()
.UseInMemoryDatabase(Guid.NewGuid().ToString())
.Options;
var context = new ImContext(options);
return context;
}
// ---------- 可用于模拟 SaveChanges 抛异常的 DbContext ----------
private class ThrowOnSaveImContext : ImContext
{
private readonly bool _throw;
public ThrowOnSaveImContext(DbContextOptions<ImContext> options, bool throwOnSave) : base(options)
{
_throw = throwOnSave;
}
public override Task<int> SaveChangesAsync(CancellationToken cancellationToken = default)
{
if (_throw) throw new InvalidOperationException("Simulated DB save error");
return base.SaveChangesAsync(cancellationToken);
}
}
// ---------- 默认 AutoMapper简易映射配置 ----------
private IMapper CreateMapper()
{
var config = new MapperConfiguration(cfg =>
{
cfg.CreateMap<RegisterRequestDto, User>();
cfg.CreateMap<User, UserInfoDto>();
});
return config.CreateMapper();
}
// ---------- 创建 Service允许注入自定义 mapper ----------
private AuthService CreateService(ImContext context, IMapper mapper = null)
{
var loggerMock = new Mock<ILogger<AuthService>>();
var mapperToUse = mapper ?? CreateMapper();
var mockCache = new Mock<ICacheService>();
return new AuthService(context, loggerMock.Object, mapperToUse,mockCache.Object);
}
// --------------------- 测试用例 ---------------------
[Fact]
public async Task LoginAsync_ShouldReturnUser_WhenValidCredentials()
{
var context = CreateDbContext();
context.Users.Add(new User { Id = 1, Username = "Tom", Password = "123456", NickName = "测试用户" });
await context.SaveChangesAsync();
var service = CreateService(context);
var result = await service.LoginAsync(new LoginRequestDto
{
Username = "Tom",
Password = "123456"
});
Assert.NotNull(result);
Assert.Equal(1, result.Id);
Assert.Equal("Tom", result.Username);
}
[Fact]
public async Task LoginAsync_ShouldThrowException_WhenInvalidCredentials()
{
var context = CreateDbContext();
var service = CreateService(context);
await Assert.ThrowsAsync<BaseException>(async () =>
{
await service.LoginAsync(new LoginRequestDto
{
Username = "NotExist",
Password = "wrong"
});
});
}
[Fact]
public async Task LoginAsync_IsCaseSensitive_ForUsername()
{
// 说明:当前实现是精确匹配 => 区分大小写
var context = CreateDbContext();
context.Users.Add(new User { Id = 10, Username = "Tom", Password = "p" });
await context.SaveChangesAsync();
var service = CreateService(context);
// 使用小写用户名应失败(表明当前逻辑区分大小写)
await Assert.ThrowsAsync<BaseException>(async () =>
{
await service.LoginAsync(new LoginRequestDto { Username = "tom", Password = "p" });
});
}
[Fact]
public async Task RegisterAsync_ShouldPersistUserAndReturnDto_WhenNewUser()
{
var context = CreateDbContext();
var service = CreateService(context);
var request = new RegisterRequestDto
{
Username = "Jerry",
Password = "123456"
};
var result = await service.RegisterAsync(request);
// 返回值正确
Assert.NotNull(result);
Assert.Equal("Jerry", result.Username);
Assert.True(result.Id > 0);
// DB 中确实存在该用户
var persisted = await context.Users.FirstOrDefaultAsync(u => u.Username == "Jerry");
Assert.NotNull(persisted);
Assert.Equal("Jerry", persisted.Username);
}
[Fact]
public async Task RegisterAsync_ShouldThrowException_WhenUserExists()
{
var context = CreateDbContext();
context.Users.Add(new User { Username = "Tom", Password = "12223" });
await context.SaveChangesAsync();
var service = CreateService(context);
var request = new RegisterRequestDto { Username = "Tom", Password = "123" };
await Assert.ThrowsAsync<BaseException>(() => service.RegisterAsync(request));
}
[Fact]
public async Task RegisterAsync_ShouldThrow_WhenMapperThrows()
{
var context = CreateDbContext();
var mapperMock = new Mock<IMapper>();
mapperMock.Setup(m => m.Map<User>(It.IsAny<RegisterRequestDto>()))
.Throws(new Exception("mapper failure"));
var service = CreateService(context, mapperMock.Object);
var req = new RegisterRequestDto { Username = "A", Password = "B" };
var ex = await Assert.ThrowsAsync<Exception>(() => service.RegisterAsync(req));
Assert.Contains("mapper failure", ex.Message);
}
[Fact]
public async Task RegisterAsync_ShouldPropagateSaveException_WhenSaveFails()
{
var options = new DbContextOptionsBuilder<ImContext>()
.UseInMemoryDatabase(Guid.NewGuid().ToString())
.Options;
// 使用自定义上下文在 SaveChanges 时抛异常
var throwingContext = new ThrowOnSaveImContext(options, throwOnSave: true);
var service = CreateService(throwingContext);
var req = new RegisterRequestDto { Username = "X", Password = "P" };
var ex = await Assert.ThrowsAsync<InvalidOperationException>(() => service.RegisterAsync(req));
Assert.Contains("Simulated DB save error", ex.Message);
}
[Fact]
public async Task RegisterAsync_NullDto_ThrowsNullReferenceException_CurrentBehavior()
{
// 说明:当前实现没有对 dto 为 null 做检查,会触发 NullReferenceException。
// 建议:在生产代码中改为抛 ArgumentNullException 或者进行参数校验并返回明确错误。
var context = CreateDbContext();
var service = CreateService(context);
await Assert.ThrowsAsync<NullReferenceException>(async () =>
{
await service.RegisterAsync(null);
});
}
}