connection = Mockery::mock(ConnectionInterface::class); $this->auditLogger = Mockery::mock(VaultAuditLogger::class); // Generate test encryption key $this->encryptionKey = DatabaseVault::generateEncryptionKey(); $this->vault = new DatabaseVault( connection: $this->connection, encryptionKey: $this->encryptionKey, auditLogger: $this->auditLogger ); }); afterEach(function () { Mockery::close(); }); describe('SecretKey Value Object', function () { it('creates valid secret key', function () { $key = SecretKey::from('database.password'); expect($key->value)->toBe('database.password'); }); it('rejects empty key', function () { expect(fn () => SecretKey::from('')) ->toThrow(InvalidArgumentException::class, 'Secret key cannot be empty'); }); it('rejects invalid characters', function () { expect(fn () => SecretKey::from('invalid key!')) ->toThrow(InvalidArgumentException::class); }); it('accepts valid characters', function () { $key = SecretKey::from('api.stripe.secret-key_2024'); expect($key->value)->toBe('api.stripe.secret-key_2024'); }); }); describe('SecretValue Value Object', function () { it('stores secret value', function () { $value = new SecretValue('my-secret-password'); expect($value->reveal())->toBe('my-secret-password'); }); it('masks value in __toString', function () { $value = new SecretValue('my-secret-password'); expect((string) $value)->toBe('[SECRET]'); }); it('redacts value in var_dump', function () { $value = new SecretValue('my-secret-password'); $debugInfo = $value->__debugInfo(); expect($debugInfo['value'])->toBe('[REDACTED]'); expect($debugInfo['length'])->toBe(18); }); it('checks if value is empty', function () { $empty = new SecretValue(''); $notEmpty = new SecretValue('value'); expect($empty->isEmpty())->toBeTrue(); expect($notEmpty->isEmpty())->toBeFalse(); }); }); describe('DatabaseVault Key Generation', function () { it('generates valid encryption key', function () { $key = DatabaseVault::generateEncryptionKey(); expect(strlen($key))->toBe(SODIUM_CRYPTO_SECRETBOX_KEYBYTES); }); it('encodes and decodes key correctly', function () { $originalKey = DatabaseVault::generateEncryptionKey(); $encoded = DatabaseVault::encodeKey($originalKey); $decoded = DatabaseVault::decodeKey($encoded); expect($decoded)->toBe($originalKey); }); }); describe('DatabaseVault Basic Operations', function () { it('requires correct key length', function () { $connection = Mockery::mock(ConnectionInterface::class); $auditLogger = Mockery::mock(VaultAuditLogger::class); expect(function () use ($connection, $auditLogger) { new DatabaseVault( connection: $connection, encryptionKey: 'too-short', auditLogger: $auditLogger ); })->toThrow(InvalidArgumentException::class); }); it('checks if libsodium is available', function () { if (! extension_loaded('sodium')) { $this->markTestSkipped('Sodium extension not available'); } expect(extension_loaded('sodium'))->toBeTrue(); }); });