Files
breadcrumb-the-shire/tests/Service/Auth/AuthCryptoGatewayTest.php

127 lines
4.1 KiB
PHP
Raw Normal View History

<?php
namespace MintyPHP\Tests\Service\Auth;
use MintyPHP\Service\Auth\AuthCryptoGateway;
use PHPUnit\Framework\TestCase;
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));
}
$this->gateway = new AuthCryptoGateway();
}
// --- isConfigured ---
public function testIsConfiguredReturnsTrueWhenKeyIsSet(): void
{
$this->assertTrue($this->gateway->isConfigured());
}
// --- encryptString / decryptString round-trip ---
public function testEncryptDecryptRoundTrip(): void
{
$plaintext = 'my-secret-sso-value';
$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']);
}
}