Сгруппировать тестовые проекты по областям
Deal.Tests.Unit, Deal.Telegram.Tests, Deal.Ai.Tests, Deal.Ml.Tests разложены по областям (Modules/<X>, Api, Infrastructure, Contracts, Grpc, ...), общие хелперы -> Support; namespace = папка, using между областями добавлены итеративно по ошибкам сборки.
This commit is contained in:
@@ -0,0 +1,95 @@
|
||||
using Deal.Infrastructure.Security;
|
||||
using Deal.Modules.Settings.Application.Abstractions;
|
||||
using Deal.Modules.Settings.Application.Models;
|
||||
using Deal.Modules.Settings.Application.Registrars;
|
||||
using Deal.Modules.Settings.Application.Services;
|
||||
using Deal.Tests.Unit.Support;
|
||||
|
||||
namespace Deal.Tests.Unit.Infrastructure;
|
||||
|
||||
/// <summary>
|
||||
/// Тесты AES-256-GCM-шифра секретов: roundtrip, nonce, устойчивость к повреждению (Task 1, Ruling 2).
|
||||
/// </summary>
|
||||
public sealed class SecretCipherTests
|
||||
{
|
||||
private const int KeySizeBytes = 32;
|
||||
|
||||
private const string SomeSecret = "sk-ant-секретный-ключ-провайдера";
|
||||
|
||||
// Фиксированный тестовый ключ (не нулевой, байты 1..32).
|
||||
private static readonly byte[] Key =
|
||||
Enumerable.Range(1, KeySizeBytes).Select(index => (byte)index).ToArray();
|
||||
|
||||
private readonly ISecretCipher _cipher = new AesGcmSecretCipher(Key);
|
||||
|
||||
[Theory]
|
||||
[InlineData("")]
|
||||
[InlineData("простой текст")]
|
||||
[InlineData("кириллица и спецсимволы: !@#$%^&*()_+=-")]
|
||||
[InlineData("https://api.anthropic.com/v1?key=тоже-секрет")]
|
||||
public void Decrypt_AfterEncrypt_ReturnsOriginalText(string plainText)
|
||||
{
|
||||
string encrypted = _cipher.Encrypt(plainText);
|
||||
|
||||
Assert.Equal(plainText, _cipher.Decrypt(encrypted));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Encrypt_EmptyText_ReturnsEmptyString()
|
||||
{
|
||||
Assert.Equal(string.Empty, _cipher.Encrypt(string.Empty));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Encrypt_NonEmptyText_ProducesTokenWithEncPrefix()
|
||||
{
|
||||
string token = _cipher.Encrypt(SomeSecret);
|
||||
|
||||
Assert.StartsWith("enc:", token);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Encrypt_SameTextTwice_ProducesDifferentTokensBecauseOfNonce()
|
||||
{
|
||||
string first = _cipher.Encrypt(SomeSecret);
|
||||
string second = _cipher.Encrypt(SomeSecret);
|
||||
|
||||
Assert.NotEqual(first, second);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Decrypt_TokenWithoutEncPrefix_ReturnsEmptyString()
|
||||
{
|
||||
Assert.Equal(string.Empty, _cipher.Decrypt("незашифрованное значение"));
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("enc:")]
|
||||
[InlineData("enc:не-base64!")]
|
||||
[InlineData("enc:AAAA")]
|
||||
public void Decrypt_MalformedToken_ReturnsEmptyStringWithoutException(string token)
|
||||
{
|
||||
Assert.Equal(string.Empty, _cipher.Decrypt(token));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Decrypt_TokenWithCorruptedTag_ReturnsEmptyString()
|
||||
{
|
||||
string token = _cipher.Encrypt(SomeSecret);
|
||||
byte[] payload = Convert.FromBase64String(token[4..]);
|
||||
payload[^1] ^= 0xFF; // портим последний байт тега аутентичности
|
||||
string corruptedToken = "enc:" + Convert.ToBase64String(payload);
|
||||
|
||||
Assert.Equal(string.Empty, _cipher.Decrypt(corruptedToken));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Decrypt_TokenEncryptedWithAnotherKey_ReturnsEmptyString()
|
||||
{
|
||||
byte[] otherKey = Enumerable.Range(1, KeySizeBytes).Select(index => (byte)(index * 2)).ToArray();
|
||||
ISecretCipher otherCipher = new AesGcmSecretCipher(otherKey);
|
||||
string token = otherCipher.Encrypt(SomeSecret);
|
||||
|
||||
Assert.Equal(string.Empty, _cipher.Decrypt(token));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user