toString())->toBe($key); expect((string) $lockKey)->toBe($key); }); it('validates lock key constraints', function () { // Empty key expect(fn() => LockKey::fromString('')) ->toThrow(\InvalidArgumentException::class, 'Lock key cannot be empty'); // Too long expect(fn() => LockKey::fromString(str_repeat('a', 256))) ->toThrow(\InvalidArgumentException::class, 'Lock key cannot exceed 255 characters'); // Invalid characters expect(fn() => LockKey::fromString('invalid@key!')) ->toThrow(\InvalidArgumentException::class, 'Lock key contains invalid characters'); expect(fn() => LockKey::fromString('key with spaces')) ->toThrow(\InvalidArgumentException::class, 'Lock key contains invalid characters'); }); it('allows valid characters only', function () { $validKeys = [ 'simple-key', 'key_with_underscores', 'key.with.dots', 'key123', 'UPPERCASE-key', 'mixed-Key_123.test' ]; foreach ($validKeys as $key) { $lockKey = LockKey::fromString($key); expect($lockKey->toString())->toBe($key); } }); it('can create job-specific lock keys', function () { $jobId = JobId::generate(); $lockKey = LockKey::forJob($jobId); expect($lockKey->toString())->toStartWith('job.'); expect($lockKey->toString())->toContain($jobId->toString()); }); it('can create queue-specific lock keys', function () { $queueName = QueueName::defaultQueue(); $lockKey = LockKey::forQueue($queueName); expect($lockKey->toString())->toStartWith('queue.'); expect($lockKey->toString())->toContain($queueName->toString()); }); it('can create worker-specific lock keys', function () { $workerId = WorkerId::generate(); $lockKey = LockKey::forWorker($workerId); expect($lockKey->toString())->toStartWith('worker.'); expect($lockKey->toString())->toContain($workerId->toString()); }); it('can create resource-specific lock keys', function () { $lockKey = LockKey::forResource('database', 'user-table'); expect($lockKey->toString())->toBe('database.user-table'); }); it('can create batch-specific lock keys', function () { $batchId = 'batch-123-abc'; $lockKey = LockKey::forBatch($batchId); expect($lockKey->toString())->toBe('batch.' . $batchId); }); it('supports prefix modification', function () { $lockKey = LockKey::fromString('original.key'); $prefixed = $lockKey->withPrefix('tenant-1'); expect($prefixed->toString())->toBe('tenant-1.original.key'); expect($lockKey->toString())->toBe('original.key'); // Original unchanged }); it('supports suffix modification', function () { $lockKey = LockKey::fromString('original.key'); $suffixed = $lockKey->withSuffix('processing'); expect($suffixed->toString())->toBe('original.key.processing'); expect($lockKey->toString())->toBe('original.key'); // Original unchanged }); it('supports pattern matching', function () { $lockKey = LockKey::fromString('job.email-queue.123'); expect($lockKey->matches('job.*'))->toBeTrue(); expect($lockKey->matches('job.email-queue.*'))->toBeTrue(); expect($lockKey->matches('worker.*'))->toBeFalse(); expect($lockKey->matches('*.123'))->toBeTrue(); }); it('supports equality comparison', function () { $key = 'test.lock.key'; $lockKey1 = LockKey::fromString($key); $lockKey2 = LockKey::fromString($key); $lockKey3 = LockKey::fromString('different.key'); expect($lockKey1->equals($lockKey2))->toBeTrue(); expect($lockKey1->equals($lockKey3))->toBeFalse(); }); it('supports JSON serialization', function () { $key = 'serializable.lock.key'; $lockKey = LockKey::fromString($key); expect($lockKey->jsonSerialize())->toBe($key); expect(json_encode($lockKey))->toBe('"' . $key . '"'); }); it('can chain modifications', function () { $lockKey = LockKey::fromString('base.key') ->withPrefix('tenant-1') ->withSuffix('processing') ->withSuffix('active'); expect($lockKey->toString())->toBe('tenant-1.base.key.processing.active'); }); it('handles complex resource hierarchies', function () { // Simulate nested resource locks $databaseLock = LockKey::forResource('database', 'users'); $tableLock = $databaseLock->withSuffix('table-lock'); $rowLock = $tableLock->withSuffix('row-123'); expect($databaseLock->toString())->toBe('database.users'); expect($tableLock->toString())->toBe('database.users.table-lock'); expect($rowLock->toString())->toBe('database.users.table-lock.row-123'); // Pattern matching for hierarchical locks expect($rowLock->matches('database.users.*'))->toBeTrue(); expect($rowLock->matches('*.row-123'))->toBeTrue(); }); });