765cc632d5
Remove the Order demo (entity/feature/repo/config/gRPC/proto) and the three pre-marketplace migrations; regenerate a fresh InitialBaseline migration. Stand up the REST surface (PingController + System/Ping CQRS) proving the Mediator -> behaviors -> OperationResult -> ApiResult envelope end to end. Close wiring gaps: register LoggingBehavior (outermost) and add the built-in rate limiter (per-IP global + otp/auth/sensitive policies), placed before authentication. Add current-user + audit plumbing: ICurrentUser (HttpContext + null impls), rename BaseEntity audit fields to CreatedAt/ModifiedAt (DateTimeOffset) + CreatedById/ModifiedById, stamped by a new AuditFieldInterceptor. Introduce five cross-cutting seams (IDateTimeProvider, IFieldEncryptor, ICacheService, IObjectStorage, INotificationDispatcher) with in-memory/local mocks registered via AddCrossCuttingSeams. Add Baya.Test.Foundation (encryptor, audit interceptor, ping handler) and update docs, contracts (swagger.v1.json), handoff, report, and mocks registry.
71 lines
1.9 KiB
C#
71 lines
1.9 KiB
C#
using Baya.Infrastructure.CrossCutting.Seams;
|
|
using Microsoft.Extensions.Options;
|
|
|
|
namespace Baya.Test.Foundation;
|
|
|
|
public class SymmetricFieldEncryptorTests
|
|
{
|
|
private static SymmetricFieldEncryptor CreateEncryptor()
|
|
{
|
|
var options = Options.Create(new SeamOptions
|
|
{
|
|
FieldEncryption = new FieldEncryptionOptions
|
|
{
|
|
Key = "unit-test-field-encryption-key",
|
|
HashKey = "unit-test-hash-key"
|
|
}
|
|
});
|
|
|
|
return new SymmetricFieldEncryptor(options);
|
|
}
|
|
|
|
[Fact]
|
|
public void Decrypt_OfEncrypt_ReturnsOriginalPlaintext()
|
|
{
|
|
// Arrange
|
|
var encryptor = CreateEncryptor();
|
|
const string plaintext = "09123456789";
|
|
|
|
// Act
|
|
var cipher = encryptor.Encrypt(plaintext);
|
|
var roundTripped = encryptor.Decrypt(cipher);
|
|
|
|
// Assert
|
|
Assert.NotEqual(plaintext, cipher);
|
|
Assert.Equal(plaintext, roundTripped);
|
|
}
|
|
|
|
[Fact]
|
|
public void Encrypt_SameInputTwice_ProducesDifferentCiphertext()
|
|
{
|
|
// Arrange — a random IV per call means ciphertext is not deterministic (semantic security).
|
|
var encryptor = CreateEncryptor();
|
|
const string plaintext = "IR820540102680020817909002";
|
|
|
|
// Act
|
|
var first = encryptor.Encrypt(plaintext);
|
|
var second = encryptor.Encrypt(plaintext);
|
|
|
|
// Assert
|
|
Assert.NotEqual(first, second);
|
|
Assert.Equal(plaintext, encryptor.Decrypt(first));
|
|
Assert.Equal(plaintext, encryptor.Decrypt(second));
|
|
}
|
|
|
|
[Fact]
|
|
public void Hash_IsDeterministic_ForLookups()
|
|
{
|
|
// Arrange
|
|
var encryptor = CreateEncryptor();
|
|
const string value = "IR820540102680020817909002";
|
|
|
|
// Act
|
|
var first = encryptor.Hash(value);
|
|
var second = encryptor.Hash(value);
|
|
|
|
// Assert
|
|
Assert.Equal(first, second);
|
|
Assert.NotEqual(value, first);
|
|
}
|
|
}
|