mirror of
https://github.com/dotnet/skills.git
synced 2026-09-20 09:49:54 +08:00
948a9a52ef
* Add exp-mock-usage-analysis skill and evaluation New experimental skill that detects unused, redundant, or unnecessary mocks in .NET test suites. Covers Moq, NSubstitute, FakeItEasy, and manual test doubles. - SKILL.md with 6-step workflow: gather code, classify mocks, detect anti-patterns, incorporate runtime data, generate recommendations, report findings - eval.yaml with 10 scenarios: trivial type mocking, unused setups, redundant configs, stable utility mocks, well-placed mocks (no false positives), NSubstitute support, FakeItEasy support, setup sprawl, and 2 non-activation boundary tests - Fixture files for each scenario with production + test code - CODEOWNERS entry for @dotnet/dotnet-testing * Remove no-headroom scenarios, increase NSubstitute timeout Remove 4 scenarios where baseline already scores 5.0/5 (model handles these well without skill guidance): - Detect mocking of DTOs, records, and enums - Recognize well-placed mocks without inventing false positives - Decline when asked about non-mock test anti-patterns - Decline mock framework migration request Increase NSubstitute scenario timeout from 120s to 180s to avoid baseline timeouts impacting scoring. Remaining 6 scenarios all show positive skill impact (3→5, 3→4, etc). * Document areas the model handles well without the skill Add a subsection under 'When Not to Use' listing mock-related tasks where the base model already scores 5.0/5 without skill guidance: - Mocking trivial types (DTOs, records, enums) - Recognizing well-placed mocks (no false positives) * Fix md * Improve
76 lines
2.2 KiB
C#
76 lines
2.2 KiB
C#
using Microsoft.Extensions.Logging;
|
|
using Microsoft.Extensions.Options;
|
|
|
|
namespace UserManagement;
|
|
|
|
public class UserManagerOptions
|
|
{
|
|
public int MaxLoginAttempts { get; set; } = 5;
|
|
public TimeSpan LockoutDuration { get; set; } = TimeSpan.FromMinutes(15);
|
|
}
|
|
|
|
public interface IUserStore
|
|
{
|
|
User? FindByEmail(string email);
|
|
void Update(User user);
|
|
}
|
|
|
|
public interface IPasswordHasher
|
|
{
|
|
bool Verify(string password, string hash);
|
|
}
|
|
|
|
public record User(int Id, string Email, string PasswordHash, int FailedAttempts, DateTime? LockedUntil);
|
|
|
|
public class AuthenticationService
|
|
{
|
|
private readonly IUserStore _userStore;
|
|
private readonly IPasswordHasher _hasher;
|
|
private readonly ILogger<AuthenticationService> _logger;
|
|
private readonly IOptions<UserManagerOptions> _options;
|
|
|
|
public AuthenticationService(
|
|
IUserStore userStore,
|
|
IPasswordHasher hasher,
|
|
ILogger<AuthenticationService> logger,
|
|
IOptions<UserManagerOptions> options)
|
|
{
|
|
_userStore = userStore;
|
|
_hasher = hasher;
|
|
_logger = logger;
|
|
_options = options;
|
|
}
|
|
|
|
public bool Login(string email, string password)
|
|
{
|
|
var user = _userStore.FindByEmail(email);
|
|
if (user is null)
|
|
{
|
|
_logger.LogWarning("Login attempt for unknown email: {Email}", email);
|
|
return false;
|
|
}
|
|
|
|
if (user.LockedUntil.HasValue && user.LockedUntil > DateTime.UtcNow)
|
|
{
|
|
_logger.LogWarning("Login attempt for locked account: {Email}", email);
|
|
return false;
|
|
}
|
|
|
|
if (!_hasher.Verify(password, user.PasswordHash))
|
|
{
|
|
var attempts = user.FailedAttempts + 1;
|
|
DateTime? lockUntil = attempts >= _options.Value.MaxLoginAttempts
|
|
? DateTime.UtcNow.Add(_options.Value.LockoutDuration)
|
|
: null;
|
|
|
|
_userStore.Update(user with { FailedAttempts = attempts, LockedUntil = lockUntil });
|
|
_logger.LogWarning("Failed login for {Email}. Attempt {Attempt}", email, attempts);
|
|
return false;
|
|
}
|
|
|
|
_userStore.Update(user with { FailedAttempts = 0, LockedUntil = null });
|
|
_logger.LogInformation("Successful login for {Email}", email);
|
|
return true;
|
|
}
|
|
}
|