using Omika.Diagnostics.Users;
namespace Omika.Diagnostics.Tests.Fakes;
///
/// Fake en mémoire de pour les tests sans PostgreSQL.
/// Reproduit la sémantique du repository Npgsql : unicité insensible à la casse, tri par
/// username, hash stocké (jamais de mot de passe en clair).
///
public sealed class FakeDiagnosticUserRepository : IDiagnosticUserRepository
{
private readonly List _users = [];
private long _nextId = 1;
public IReadOnlyList Seed(params DiagnosticUserRecord[] users)
{
foreach (DiagnosticUserRecord u in users)
{
if (u.Id == 0)
{
u.Id = _nextId++;
}
else
{
_nextId = Math.Max(_nextId, u.Id + 1);
}
_users.Add(u);
}
return _users;
}
public Task GetByUsernameAsync(
string username,
CancellationToken cancellationToken = default)
{
string? normalized = username?.Trim();
DiagnosticUserRecord? found = _users.FirstOrDefault(u =>
string.Equals(u.Username, normalized, StringComparison.OrdinalIgnoreCase));
return Task.FromResult(found);
}
public Task> ListAsync(
CancellationToken cancellationToken = default)
{
IReadOnlyList ordered = _users
.OrderBy(u => u.Username, StringComparer.OrdinalIgnoreCase)
.ToList();
return Task.FromResult(ordered);
}
public Task CreateAsync(
string username,
string passwordHash,
string role,
CancellationToken cancellationToken = default)
{
string? normalized = username?.Trim();
if (string.IsNullOrWhiteSpace(normalized))
{
return Task.FromResult(null);
}
if (_users.Any(u => string.Equals(u.Username, normalized, StringComparison.OrdinalIgnoreCase)))
{
return Task.FromResult(null); // doublon.
}
DateTimeOffset now = DateTimeOffset.UtcNow;
var record = new DiagnosticUserRecord
{
Id = _nextId++,
Username = normalized,
PasswordHash = passwordHash,
Role = role,
Enabled = true,
CreatedAtUtc = now,
UpdatedAtUtc = now,
PasswordChangedAtUtc = now,
LastLoginAtUtc = null
};
_users.Add(record);
return Task.FromResult(record);
}
public Task SetEnabledAsync(
long id,
bool enabled,
CancellationToken cancellationToken = default)
{
DiagnosticUserRecord? user = _users.FirstOrDefault(u => u.Id == id);
if (user is null)
{
return Task.FromResult(false);
}
user.Enabled = enabled;
user.UpdatedAtUtc = DateTimeOffset.UtcNow;
return Task.FromResult(true);
}
public Task SetRoleAsync(
long id,
string role,
CancellationToken cancellationToken = default)
{
DiagnosticUserRecord? user = _users.FirstOrDefault(u => u.Id == id);
if (user is null)
{
return Task.FromResult(false);
}
user.Role = role;
user.UpdatedAtUtc = DateTimeOffset.UtcNow;
return Task.FromResult(true);
}
public Task UpdatePasswordHashAsync(
long id,
string passwordHash,
CancellationToken cancellationToken = default)
{
DiagnosticUserRecord? user = _users.FirstOrDefault(u => u.Id == id);
if (user is null)
{
return Task.FromResult(false);
}
user.PasswordHash = passwordHash;
user.PasswordChangedAtUtc = DateTimeOffset.UtcNow;
user.UpdatedAtUtc = DateTimeOffset.UtcNow;
return Task.FromResult(true);
}
public Task UpdateLastLoginAsync(
string username,
CancellationToken cancellationToken = default)
{
DiagnosticUserRecord? user = _users.FirstOrDefault(u =>
string.Equals(u.Username, username?.Trim(), StringComparison.OrdinalIgnoreCase));
if (user is not null)
{
user.LastLoginAtUtc = DateTimeOffset.UtcNow;
}
return Task.CompletedTask;
}
}