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; /// /// Тесты AES-256-GCM-шифра секретов: roundtrip, nonce, устойчивость к повреждению (Task 1, Ruling 2). /// 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)); } }