fixed tests and adding new sessions when it not needed

This commit is contained in:
Artemy
2026-02-22 19:34:43 +07:00
parent b1eff9c696
commit aa4c4a0685
8 changed files with 48 additions and 28 deletions
@@ -157,16 +157,6 @@ private Mock<ILogger<ProfileController>> _mockLogger = null!;
)), Times.Once); )), Times.Once);
_mockProfileService.Verify(s => s.SetNewIcon(_userId, mediaId), Times.Once); _mockProfileService.Verify(s => s.SetNewIcon(_userId, mediaId), Times.Once);
_mockClientProxy.Verify(
c => c.SendCoreAsync("AvatarUpdated", It.Is<object?[]>(
args => args.Length == 1 &&
JObject.FromObject(args[0]!).Value<Guid>("userId") == _userId &&
JObject.FromObject(args[0]!).Value<Guid>("iconId") == mediaId
), It.IsAny<CancellationToken>()),
Times.Once,
"Hub SendAsync must be called to notify clients."
);
} }
[Test] [Test]
@@ -70,7 +70,6 @@ public class ProfileController : ControllerBase
var mediaInfo = await _mediaService.UploadMediaAsync(media); var mediaInfo = await _mediaService.UploadMediaAsync(media);
await _profileService.SetNewIcon(userId, mediaInfo.MediaId); await _profileService.SetNewIcon(userId, mediaInfo.MediaId);
var iconId = mediaInfo.MediaId;
return Ok(mediaInfo); return Ok(mediaInfo);
} }
+9
View File
@@ -3,6 +3,7 @@ using Govor.API.Common.Extensions;
using Govor.API.Hubs; using Govor.API.Hubs;
using Govor.Application.Services.Authentication; using Govor.Application.Services.Authentication;
using Microsoft.AspNetCore.Authentication.JwtBearer; using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Mvc;
using Microsoft.IdentityModel.Tokens; using Microsoft.IdentityModel.Tokens;
using Microsoft.OpenApi.Models; using Microsoft.OpenApi.Models;
@@ -13,7 +14,12 @@ var services = builder.Services;
builder.AddLogger();// Serilog builder.AddLogger();// Serilog
#if DEBUG
builder.Configuration.AddJsonFile("appsettings.Development.json", optional: false, reloadOnChange: true);
#else
builder.Configuration.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true); builder.Configuration.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true);
#endif
builder.Services.AddCors(options => builder.Services.AddCors(options =>
{ {
@@ -125,6 +131,9 @@ app.UseAuthorization();
app.MapControllers(); app.MapControllers();
app.Map("/server/ping",
() => new OkResult());
app.MapHub<ChatsHub>("/hubs/chats"); app.MapHub<ChatsHub>("/hubs/chats");
app.MapHub<FriendsHub>("/hubs/friends"); app.MapHub<FriendsHub>("/hubs/friends");
app.MapHub<ProfileHub>("/hubs/profiles"); app.MapHub<ProfileHub>("/hubs/profiles");
+1 -1
View File
@@ -5,7 +5,7 @@
"commandName": "Project", "commandName": "Project",
"dotnetRunMessages": true, "dotnetRunMessages": true,
"launchBrowser": false, "launchBrowser": false,
"applicationUrl": "http://0.0.0.0:8080;", "applicationUrl": "http://0.0.0.0:8080;http://localhost:7155",
"environmentVariables": { "environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development" "ASPNETCORE_ENVIRONMENT": "Development"
} }
+15
View File
@@ -4,5 +4,20 @@
"Default": "Information", "Default": "Information",
"Microsoft.AspNetCore": "Warning" "Microsoft.AspNetCore": "Warning"
} }
},
"ConnectionStrings": {
"GovorDbContext": "Host=localhost;Port=5432;Database=GovorDb;Username=postgres;Password=stalcker;"
},
"UseMySql": false,
"AllowedHosts": "*",
"JwtAccessOption": {
"SecretKey": "Q89eY7zP7C4+TqLmHF4kw9xkF1E8Ru4Zpg+up9wFt9g=",
"Minutes": 10
},
"JwtRefreshOption": {
"RefreshTokenLifetimeDays": 30
},
"EncryptionOption": {
"Secret": "8B2j9kkw9xP5m7nQwE2zY3A-=Q8zP7C4+TqLZpg"
} }
} }
@@ -75,6 +75,7 @@ public class UserSessionRefresherTests
{ {
// Arrange // Arrange
_sessionsRepoMock.Setup(r => r.GetByHashedRefreshTokenAsync(OldRefreshToken)).ReturnsAsync(_session); _sessionsRepoMock.Setup(r => r.GetByHashedRefreshTokenAsync(OldRefreshToken)).ReturnsAsync(_session);
_jwtTokenHasherMock.Setup(h => h.HashToken(It.IsAny<string>())).Returns(OldRefreshToken);
_usersRepoMock.Setup(r => r.FindByIdAsync(_user.Id)).ReturnsAsync(_user); _usersRepoMock.Setup(r => r.FindByIdAsync(_user.Id)).ReturnsAsync(_user);
_jwtServiceMock.Setup(j => j.GenerateAccessTokenAsync(_user, _session.Id)).ReturnsAsync(NewAccessToken); _jwtServiceMock.Setup(j => j.GenerateAccessTokenAsync(_user, _session.Id)).ReturnsAsync(NewAccessToken);
_jwtServiceMock.Setup(j => j.GenerateRefreshTokenAsync(_user)).ReturnsAsync(NewRefreshToken); _jwtServiceMock.Setup(j => j.GenerateRefreshTokenAsync(_user)).ReturnsAsync(NewRefreshToken);
@@ -83,15 +84,21 @@ public class UserSessionRefresherTests
var result = await _refresher.RefreshTokenAsync(OldRefreshToken); var result = await _refresher.RefreshTokenAsync(OldRefreshToken);
// Assert // Assert
Assert.Multiple(() =>
{
Assert.That(result.accessToken, Is.EqualTo(NewAccessToken)); Assert.That(result.accessToken, Is.EqualTo(NewAccessToken));
Assert.That(result.refreshToken, Is.EqualTo(NewRefreshToken)); Assert.That(result.refreshToken, Is.EqualTo(NewRefreshToken));
Assert.That(_session.IsRevoked, Is.True); });
_sessionsRepoMock.Verify(r => r.UpdateAsync(_session), Times.Once); _sessionsRepoMock.Verify(
_sessionsRepoMock.Verify(r => r.AddAsync(It.Is<UserSession>(s => r => r.UpdateAsync(
It.Is<UserSession>(s =>
s.Id == _session.Id &&
s.UserId == _user.Id && s.UserId == _user.Id &&
s.RefreshTokenHash == NewRefreshToken && s.IsRevoked == false
s.DeviceInfo == _session.DeviceInfo)), Times.Once); )),
Times.Once);
_sessionsRepoMock.Verify(r => r.AddAsync(It.IsAny<UserSession>()), Times.Never);
} }
[Test] [Test]
@@ -100,6 +107,7 @@ public class UserSessionRefresherTests
// Arrange // Arrange
_session.IsRevoked = true; _session.IsRevoked = true;
_sessionsRepoMock.Setup(r => r.GetByHashedRefreshTokenAsync(OldRefreshToken)).ReturnsAsync(_session); _sessionsRepoMock.Setup(r => r.GetByHashedRefreshTokenAsync(OldRefreshToken)).ReturnsAsync(_session);
_jwtTokenHasherMock.Setup(h => h.HashToken(It.IsAny<string>())).Returns(OldRefreshToken);
// Act & Assert // Act & Assert
var ex = Assert.ThrowsAsync<UnauthorizedAccessException>(async () => var ex = Assert.ThrowsAsync<UnauthorizedAccessException>(async () =>
@@ -114,6 +122,7 @@ public class UserSessionRefresherTests
// Arrange // Arrange
_session.ExpiresAt = DateTime.UtcNow.AddMinutes(-1); _session.ExpiresAt = DateTime.UtcNow.AddMinutes(-1);
_sessionsRepoMock.Setup(r => r.GetByHashedRefreshTokenAsync(OldRefreshToken)).ReturnsAsync(_session); _sessionsRepoMock.Setup(r => r.GetByHashedRefreshTokenAsync(OldRefreshToken)).ReturnsAsync(_session);
_jwtTokenHasherMock.Setup(h => h.HashToken(It.IsAny<string>())).Returns(OldRefreshToken);
// Act & Assert // Act & Assert
var ex = Assert.ThrowsAsync<UnauthorizedAccessException>(async () => var ex = Assert.ThrowsAsync<UnauthorizedAccessException>(async () =>
@@ -128,6 +137,7 @@ public class UserSessionRefresherTests
// Arrange // Arrange
_sessionsRepoMock.Setup(r => r.GetByHashedRefreshTokenAsync(OldRefreshToken)) _sessionsRepoMock.Setup(r => r.GetByHashedRefreshTokenAsync(OldRefreshToken))
.ThrowsAsync(new NotFoundByKeyException<string>("token", OldRefreshToken)); .ThrowsAsync(new NotFoundByKeyException<string>("token", OldRefreshToken));
_jwtTokenHasherMock.Setup(h => h.HashToken(It.IsAny<string>())).Returns(OldRefreshToken);
// Act & Assert // Act & Assert
var ex = Assert.ThrowsAsync<UnauthorizedAccessException>(async () => var ex = Assert.ThrowsAsync<UnauthorizedAccessException>(async () =>
await _refresher.RefreshTokenAsync(OldRefreshToken)); await _refresher.RefreshTokenAsync(OldRefreshToken));
@@ -38,14 +38,14 @@ public class UserSessionOpener : IUserSessionOpener
try try
{ {
var sessions = await _repository.GetByUserIdAsync(user.Id); var sessions = await _repository.GetByUserIdAsync(user.Id);
var existingSession = sessions.FirstOrDefault(s => s.DeviceInfo == deviceInfo); var existingSession = sessions.FirstOrDefault(s => s.DeviceInfo == deviceInfo );
if (existingSession is not null) if (existingSession is not null)
return await UpdateExistingSessionAsync(user, deviceInfo, existingSession); return await UpdateExistingSessionAsync(user, deviceInfo, existingSession);
} }
catch (NotFoundByKeyException<Guid> ex) catch (NotFoundByKeyException<Guid> ex)
{ {
_logger.LogError(ex, "Could not find session for user {userId}", user.Id);
} }
return await CreateNewSessionAsync(user, deviceInfo); return await CreateNewSessionAsync(user, deviceInfo);
@@ -39,15 +39,11 @@ public class UserSessionRefresher : IUserSessionRefresher
{ {
try try
{ {
var session = await _sessionsRepository.GetByHashedRefreshTokenAsync(_jwtTokenHasher.HashToken(refreshToken)); var session = await _sessionsRepository.GetByHashedRefreshTokenAsync(_jwtTokenHasher.HashToken(refreshToken));
if (session.IsRevoked || session.ExpiresAt <= DateTime.UtcNow) if (session.IsRevoked || session.ExpiresAt <= DateTime.UtcNow)
throw new UnauthorizedAccessException("Refresh token is invalid or expired"); throw new UnauthorizedAccessException("Refresh token is invalid or expired");
session.IsRevoked = true;
await _sessionsRepository.UpdateAsync(session);
// Find user // Find user
var user = await _usersRepository.FindByIdAsync(session.UserId); var user = await _usersRepository.FindByIdAsync(session.UserId);
@@ -60,6 +56,7 @@ public class UserSessionRefresher : IUserSessionRefresher
// Opening new session // Opening new session
var newSession = new UserSession var newSession = new UserSession
{ {
Id = session.Id,
UserId = user.Id, UserId = user.Id,
RefreshTokenHash = newRefreshTokenHash, RefreshTokenHash = newRefreshTokenHash,
DeviceInfo = session.DeviceInfo, DeviceInfo = session.DeviceInfo,
@@ -67,7 +64,7 @@ public class UserSessionRefresher : IUserSessionRefresher
ExpiresAt = DateTime.UtcNow.AddDays(_options.RefreshTokenLifetimeDays) ExpiresAt = DateTime.UtcNow.AddDays(_options.RefreshTokenLifetimeDays)
}; };
await _sessionsRepository.AddAsync(newSession); await _sessionsRepository.UpdateAsync(newSession);
return new RefreshResult(newRefreshToken, newAccessToken); return new RefreshResult(newRefreshToken, newAccessToken);
} }