expectException(\RuntimeException::class); Crypto::decryptString($tampered); } public function testEncryptEmptyStringProducesMalformedPayloadOnDecrypt(): void { $encrypted = Crypto::encryptString(''); $this->expectException(\RuntimeException::class); $this->expectExceptionMessage('malformed'); Crypto::decryptString($encrypted); } public function testDecryptThrowsOnEmptyString(): void { $this->expectException(\RuntimeException::class); Crypto::decryptString(''); } public function testDecryptThrowsOnMissingPayloadFields(): void { // Valid base64 wrapping valid JSON, but missing required fields. $payload = base64_encode(json_encode(['v' => 1])); $this->expectException(\RuntimeException::class); $this->expectExceptionMessage('malformed'); Crypto::decryptString($payload); } public function testRoundTripWithSpecialCharacters(): void { $values = [ 'unicode: Ünterström ñ 日本語', "newlines:\nand\ttabs", 'json-like: {"key": "value"}', str_repeat('x', 10000), ]; foreach ($values as $plaintext) { $encrypted = Crypto::encryptString($plaintext); self::assertSame($plaintext, Crypto::decryptString($encrypted), "Round-trip failed for: " . substr($plaintext, 0, 40)); } } public function testRoundTripWithBinaryLikeContent(): void { $plaintext = "null-bytes:\x00\x01\x02 and high-bytes:\xFE\xFF"; $encrypted = Crypto::encryptString($plaintext); self::assertSame($plaintext, Crypto::decryptString($encrypted)); } public function testDecryptThrowsOnNonBase64Input(): void { $this->expectException(\RuntimeException::class); Crypto::decryptString('not!valid@base64###'); } public function testDecryptThrowsOnTruncatedPayload(): void { $encrypted = Crypto::encryptString('secret'); $truncated = substr($encrypted, 0, (int) (strlen($encrypted) / 2)); $this->expectException(\RuntimeException::class); Crypto::decryptString($truncated); } }