99d25adbb1
Backend CI / build-and-test (push) Failing after 13m11s
🚀 Create and publish a Docker image / Detect changes in backend and frontend (push) Failing after 10m12s
Frontend CI / build-and-check (push) Failing after 16m9s
🚀 Create and publish a Docker image / Build & publish frontend image (push) Failing after 14m6s
🚀 Create and publish a Docker image / Build & publish backend image (push) Failing after 14m58s
🚀 Create and publish a Docker image / Update stack on Portainer (push) Failing after 14m58s
139 lines
5.2 KiB
C#
139 lines
5.2 KiB
C#
using Microsoft.EntityFrameworkCore;
|
|
using Microsoft.Extensions.Configuration;
|
|
using Microsoft.Extensions.Logging.Abstractions;
|
|
using NSubstitute;
|
|
using System.IdentityModel.Tokens.Jwt;
|
|
using System.Security.Claims;
|
|
using UniVerse.Application.DTOs.Notifications;
|
|
using UniVerse.Application.Interfaces;
|
|
using UniVerse.Domain.Entities;
|
|
using UniVerse.Domain.Enums;
|
|
using UniVerse.Domain.Exceptions;
|
|
using UniVerse.Infrastructure.Data;
|
|
using UniVerse.Infrastructure.Services;
|
|
using Xunit;
|
|
|
|
namespace UniVerse.Api.Tests.Auth;
|
|
|
|
public class AuthServiceTests
|
|
{
|
|
[Fact]
|
|
public async Task RefreshTokenAsync_InactiveUser_RevokesTokenAndThrowsForbidden()
|
|
{
|
|
await using var db = CreateDbContext();
|
|
db.Users.Add(new User
|
|
{
|
|
Id = 1,
|
|
Email = "blocked@test.local",
|
|
IsActive = false,
|
|
Roles = [new UserRoleAssignment { UserId = 1, Role = UserRole.Student }]
|
|
});
|
|
db.RefreshTokens.Add(new RefreshToken
|
|
{
|
|
Id = 1,
|
|
UserId = 1,
|
|
Token = "refresh-token",
|
|
ExpiresAt = DateTime.UtcNow.AddDays(1),
|
|
CreatedAt = DateTime.UtcNow
|
|
});
|
|
await db.SaveChangesAsync();
|
|
var service = CreateService(db);
|
|
|
|
await Assert.ThrowsAsync<ForbiddenException>(() => service.RefreshTokenAsync("refresh-token"));
|
|
|
|
var token = await db.RefreshTokens.SingleAsync(t => t.Token == "refresh-token");
|
|
Assert.NotNull(token.RevokedAt);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task GetCurrentUserAsync_InactiveUser_ThrowsForbidden()
|
|
{
|
|
await using var db = CreateDbContext();
|
|
db.Users.Add(new User
|
|
{
|
|
Id = 1,
|
|
Email = "blocked@test.local",
|
|
IsActive = false,
|
|
Roles = [new UserRoleAssignment { UserId = 1, Role = UserRole.Student }]
|
|
});
|
|
await db.SaveChangesAsync();
|
|
var service = CreateService(db);
|
|
|
|
await Assert.ThrowsAsync<ForbiddenException>(() => service.GetCurrentUserAsync(1));
|
|
}
|
|
|
|
[Fact]
|
|
public async Task LoginWithMicrosoftAsync_LinksScheduleTeacherBySubId()
|
|
{
|
|
await using var db = CreateDbContext();
|
|
db.Users.Add(new User
|
|
{
|
|
Id = 10,
|
|
Email = "modeus-person-1@modeus.local",
|
|
DisplayName = "Иванов Иван Иванович",
|
|
MicrosoftId = "sso-sub-1",
|
|
IsActive = true,
|
|
Roles = [new UserRoleAssignment { UserId = 10, Role = UserRole.Teacher }],
|
|
TeacherProfile = new TeacherProfile { UserId = 10, ModeusId = "person-1" }
|
|
});
|
|
await db.SaveChangesAsync();
|
|
var microsoftAuth = Substitute.For<IMicrosoftAuthClient>();
|
|
microsoftAuth.ExchangeAuthorizationCodeAsync("code", "http://localhost/callback", Arg.Any<CancellationToken>())
|
|
.Returns(new MicrosoftTokenResult(BuildIdToken("sso-sub-1", "teacher@sfedu.ru", "Иванов Иван Иванович")));
|
|
var service = CreateService(db, microsoftAuth);
|
|
|
|
var result = await service.LoginWithMicrosoftAsync("code", "http://localhost/callback");
|
|
|
|
Assert.Equal(10, result.Response.User.Id);
|
|
Assert.Equal("teacher@sfedu.ru", result.Response.User.Email);
|
|
Assert.Contains(UserRole.Teacher, result.Response.User.Roles);
|
|
Assert.Single(await db.Users.ToListAsync());
|
|
var user = await db.Users.Include(u => u.TeacherProfile).SingleAsync();
|
|
Assert.Equal("sso-sub-1", user.MicrosoftId);
|
|
Assert.Equal("person-1", user.TeacherProfile?.ModeusId);
|
|
}
|
|
|
|
private static AppDbContext CreateDbContext()
|
|
{
|
|
var options = new DbContextOptionsBuilder<AppDbContext>()
|
|
.UseInMemoryDatabase($"AuthServiceTests_{Guid.NewGuid()}")
|
|
.Options;
|
|
return new AppDbContext(options);
|
|
}
|
|
|
|
private static AuthService CreateService(AppDbContext db, IMicrosoftAuthClient? microsoftAuth = null)
|
|
{
|
|
var config = new ConfigurationBuilder()
|
|
.AddInMemoryCollection(new Dictionary<string, string?>
|
|
{
|
|
["Jwt:Secret"] = "test-secret-test-secret-test-secret-test-secret",
|
|
["Jwt:Issuer"] = "UniVerse.Tests",
|
|
["Jwt:Audience"] = "UniVerse.Tests",
|
|
["Jwt:AccessTokenExpirationMinutes"] = "15",
|
|
["Jwt:RefreshTokenExpirationDays"] = "30"
|
|
})
|
|
.Build();
|
|
|
|
var gamification = Substitute.For<IGamificationService>();
|
|
gamification.CalculateLevelAsync(Arg.Any<int>()).Returns(1);
|
|
|
|
var notifications = Substitute.For<INotificationService>();
|
|
notifications.SendAsync(Arg.Any<NotificationMessage>(), Arg.Any<CancellationToken>())
|
|
.Returns(Task.CompletedTask);
|
|
|
|
microsoftAuth ??= Substitute.For<IMicrosoftAuthClient>();
|
|
return new AuthService(db, config, microsoftAuth, gamification, notifications, NullLogger<AuthService>.Instance);
|
|
}
|
|
|
|
private static string BuildIdToken(string sub, string email, string name)
|
|
{
|
|
var token = new JwtSecurityToken(claims:
|
|
[
|
|
new Claim(JwtRegisteredClaimNames.Sub, sub),
|
|
new Claim("preferred_username", email),
|
|
new Claim("name", name)
|
|
]);
|
|
return new JwtSecurityTokenHandler().WriteToken(token);
|
|
}
|
|
}
|