diff --git a/tests/Service/Auth/AuthCryptoGatewayTest.php b/tests/Service/Auth/AuthCryptoGatewayTest.php index 315153a..e1e9c77 100644 --- a/tests/Service/Auth/AuthCryptoGatewayTest.php +++ b/tests/Service/Auth/AuthCryptoGatewayTest.php @@ -5,13 +5,12 @@ namespace MintyPHP\Tests\Service\Auth; use MintyPHP\Service\Auth\AuthCryptoGateway; use PHPUnit\Framework\TestCase; -class AuthCryptoGatewayTest extends TestCase +final class AuthCryptoGatewayTest extends TestCase { private AuthCryptoGateway $gateway; protected function setUp(): void { - // Define APP_CRYPTO_KEY once for the entire test run (valid 64-char hex = 32 bytes). if (!defined('APP_CRYPTO_KEY')) { define('APP_CRYPTO_KEY', str_repeat('ab', 32)); } @@ -19,108 +18,17 @@ class AuthCryptoGatewayTest extends TestCase $this->gateway = new AuthCryptoGateway(); } - // --- isConfigured --- - public function testIsConfiguredReturnsTrueWhenKeyIsSet(): void { - $this->assertTrue($this->gateway->isConfigured()); + self::assertTrue($this->gateway->isConfigured()); } - // --- encryptString / decryptString round-trip --- - - public function testEncryptDecryptRoundTrip(): void + public function testEncryptDecryptDelegatesToCrypto(): void { - $plaintext = 'my-secret-sso-value'; + $plaintext = 'auth-secret'; $encrypted = $this->gateway->encryptString($plaintext); - $this->assertNotSame($plaintext, $encrypted, 'Encrypted value must differ from plaintext'); - $this->assertNotEmpty($encrypted); - - $decrypted = $this->gateway->decryptString($encrypted); - $this->assertSame($plaintext, $decrypted); - } - - public function testEncryptEmptyStringProducesMalformedPayloadOnDecrypt(): void - { - // AES-256-GCM with empty plaintext yields empty ciphertext, - // which Crypto::decryptString rejects as malformed — expected behavior. - $encrypted = $this->gateway->encryptString(''); - - $this->expectException(\RuntimeException::class); - $this->expectExceptionMessage('malformed'); - $this->gateway->decryptString($encrypted); - } - - public function testEncryptDecryptRoundTripWithUnicode(): void - { - $plaintext = 'Ünïcödé-tëst 🔑'; - $encrypted = $this->gateway->encryptString($plaintext); - $decrypted = $this->gateway->decryptString($encrypted); - - $this->assertSame($plaintext, $decrypted); - } - - public function testEncryptDecryptRoundTripWithLongString(): void - { - $plaintext = str_repeat('long-secret-', 100); - $encrypted = $this->gateway->encryptString($plaintext); - $decrypted = $this->gateway->decryptString($encrypted); - - $this->assertSame($plaintext, $decrypted); - } - - // --- encryptString produces unique ciphertext --- - - public function testEncryptProducesDifferentCiphertextEachTime(): void - { - $plaintext = 'same-input'; - $encrypted1 = $this->gateway->encryptString($plaintext); - $encrypted2 = $this->gateway->encryptString($plaintext); - - $this->assertNotSame($encrypted1, $encrypted2, 'Each encryption should use a unique IV'); - } - - // --- decryptString error handling --- - - public function testDecryptThrowsOnInvalidBase64(): void - { - $this->expectException(\RuntimeException::class); - $this->gateway->decryptString('not-valid-base64!!!'); - } - - public function testDecryptThrowsOnCorruptedPayload(): void - { - $this->expectException(\RuntimeException::class); - $this->gateway->decryptString(base64_encode('not-json')); - } - - public function testDecryptThrowsOnTamperedCiphertext(): void - { - $encrypted = $this->gateway->encryptString('test-value'); - - // Tamper with the base64 payload — change one character in the middle - $tampered = substr($encrypted, 0, 10) . 'X' . substr($encrypted, 11); - - $this->expectException(\RuntimeException::class); - $this->gateway->decryptString($tampered); - } - - // --- Delegation to Crypto class (GR-SEC-005) --- - - public function testEncryptStringReturnsSameFormatAsCryptoClass(): void - { - $encrypted = $this->gateway->encryptString('test'); - - // The Crypto class wraps JSON in base64. Verify the outer structure. - $decoded = base64_decode($encrypted, true); - $this->assertIsString($decoded); - - $payload = json_decode($decoded, true); - $this->assertIsArray($payload); - $this->assertArrayHasKey('v', $payload); - $this->assertArrayHasKey('iv', $payload); - $this->assertArrayHasKey('tag', $payload); - $this->assertArrayHasKey('ct', $payload); - $this->assertSame(1, $payload['v']); + self::assertNotSame($plaintext, $encrypted); + self::assertSame($plaintext, $this->gateway->decryptString($encrypted)); } } diff --git a/tests/Service/Settings/SettingsCryptoGatewayTest.php b/tests/Service/Settings/SettingsCryptoGatewayTest.php index 7b50ad4..b57e72f 100644 --- a/tests/Service/Settings/SettingsCryptoGatewayTest.php +++ b/tests/Service/Settings/SettingsCryptoGatewayTest.php @@ -6,107 +6,27 @@ use MintyPHP\Service\Settings\SettingsCryptoGateway; use MintyPHP\Service\Settings\SettingsCryptoGatewayInterface; use PHPUnit\Framework\TestCase; -class SettingsCryptoGatewayTest extends TestCase +final class SettingsCryptoGatewayTest extends TestCase { - private SettingsCryptoGateway $gateway; - protected function setUp(): void { if (!defined('APP_CRYPTO_KEY')) { define('APP_CRYPTO_KEY', str_repeat('ab', 32)); } - - $this->gateway = new SettingsCryptoGateway(); } - // --- Interface compliance --- - public function testImplementsSettingsCryptoGatewayInterface(): void { - $this->assertInstanceOf(SettingsCryptoGatewayInterface::class, $this->gateway); + self::assertInstanceOf(SettingsCryptoGatewayInterface::class, new SettingsCryptoGateway()); } - // --- encryptString / decryptString round-trip --- - - public function testEncryptDecryptRoundTrip(): void + public function testEncryptDecryptDelegatesToCrypto(): void { - $plaintext = 'client-secret-for-sso'; - $encrypted = $this->gateway->encryptString($plaintext); + $gateway = new SettingsCryptoGateway(); + $plaintext = 'settings-secret'; + $encrypted = $gateway->encryptString($plaintext); - $this->assertNotSame($plaintext, $encrypted); - $this->assertNotEmpty($encrypted); - - $decrypted = $this->gateway->decryptString($encrypted); - $this->assertSame($plaintext, $decrypted); - } - - public function testEncryptEmptyStringProducesMalformedPayloadOnDecrypt(): void - { - $encrypted = $this->gateway->encryptString(''); - - $this->expectException(\RuntimeException::class); - $this->expectExceptionMessage('malformed'); - $this->gateway->decryptString($encrypted); - } - - public function testEncryptDecryptRoundTripWithSpecialChars(): void - { - $plaintext = 'p@$$w0rd!&key=val'; - $encrypted = $this->gateway->encryptString($plaintext); - $decrypted = $this->gateway->decryptString($encrypted); - - $this->assertSame($plaintext, $decrypted); - } - - // --- Unique ciphertext per encryption --- - - public function testEncryptProducesDifferentCiphertextEachTime(): void - { - $plaintext = 'same-settings-value'; - $encrypted1 = $this->gateway->encryptString($plaintext); - $encrypted2 = $this->gateway->encryptString($plaintext); - - $this->assertNotSame($encrypted1, $encrypted2); - } - - // --- Delegation to Crypto class (GR-SEC-005) --- - - public function testEncryptStringOutputIsValidCryptoPayload(): void - { - $encrypted = $this->gateway->encryptString('test'); - - $decoded = base64_decode($encrypted, true); - $this->assertIsString($decoded); - - $payload = json_decode($decoded, true); - $this->assertIsArray($payload); - $this->assertArrayHasKey('v', $payload); - $this->assertArrayHasKey('iv', $payload); - $this->assertArrayHasKey('tag', $payload); - $this->assertArrayHasKey('ct', $payload); - $this->assertSame(1, $payload['v']); - } - - // --- Error handling --- - - public function testDecryptThrowsOnInvalidPayload(): void - { - $this->expectException(\RuntimeException::class); - $this->gateway->decryptString('garbage-data'); - } - - public function testDecryptThrowsOnEmptyJsonPayload(): void - { - $this->expectException(\RuntimeException::class); - $this->gateway->decryptString(base64_encode('{}')); - } - - public function testDecryptThrowsOnTamperedCiphertext(): void - { - $encrypted = $this->gateway->encryptString('secret'); - $tampered = substr($encrypted, 0, 5) . 'Z' . substr($encrypted, 6); - - $this->expectException(\RuntimeException::class); - $this->gateway->decryptString($tampered); + self::assertNotSame($plaintext, $encrypted); + self::assertSame($plaintext, $gateway->decryptString($encrypted)); } } diff --git a/tests/Unit/Support/CryptoTest.php b/tests/Unit/Support/CryptoTest.php new file mode 100644 index 0000000..e2708cb --- /dev/null +++ b/tests/Unit/Support/CryptoTest.php @@ -0,0 +1,70 @@ +expectException(\RuntimeException::class); + Crypto::decryptString($tampered); + } + + public function testEncryptEmptyStringProducesMalformedPayloadOnDecrypt(): void + { + $encrypted = Crypto::encryptString(''); + + $this->expectException(\RuntimeException::class); + $this->expectExceptionMessage('malformed'); + Crypto::decryptString($encrypted); + } +}