- Add DISCOVERY_LOG_LEVEL=debug - Add DISCOVERY_SHOW_PROGRESS=true - Temporary changes for debugging InitializerProcessor fixes on production
178 lines
6.1 KiB
PHP
178 lines
6.1 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
use App\Framework\Ksuid\Ksuid;
|
|
|
|
it('creates KSUID from string', function () {
|
|
$value = '2SwcbqZrBNGd67ZJYmPKx42wKZj';
|
|
$ksuid = Ksuid::fromString($value);
|
|
|
|
expect($ksuid->toString())->toBe($value);
|
|
expect($ksuid->getValue())->toBe($value);
|
|
});
|
|
|
|
it('creates KSUID from bytes', function () {
|
|
$bytes = str_repeat("\x00", Ksuid::TOTAL_BYTES);
|
|
$ksuid = Ksuid::fromBytes($bytes);
|
|
|
|
expect($ksuid->getBytes())->toBe($bytes);
|
|
expect(strlen($ksuid->toString()))->toBe(Ksuid::ENCODED_LENGTH);
|
|
});
|
|
|
|
it('creates KSUID from timestamp and payload', function () {
|
|
$timestamp = time();
|
|
$payload = random_bytes(Ksuid::PAYLOAD_BYTES);
|
|
|
|
$ksuid = Ksuid::fromTimestampAndPayload($timestamp, $payload);
|
|
|
|
expect($ksuid->getTimestamp())->toBe($timestamp);
|
|
expect($ksuid->getPayload())->toBe($payload);
|
|
});
|
|
|
|
it('validates KSUID length', function () {
|
|
expect(fn () => Ksuid::fromString('toolong' . str_repeat('a', 30)))
|
|
->toThrow(InvalidArgumentException::class, 'KSUID must be exactly 27 characters long');
|
|
|
|
expect(fn () => Ksuid::fromString('tooshort'))
|
|
->toThrow(InvalidArgumentException::class, 'KSUID must be exactly 27 characters long');
|
|
});
|
|
|
|
it('validates KSUID characters', function () {
|
|
expect(fn () => Ksuid::fromString(str_repeat('!', Ksuid::ENCODED_LENGTH)))
|
|
->toThrow(InvalidArgumentException::class, 'KSUID contains invalid characters');
|
|
});
|
|
|
|
it('validates payload size', function () {
|
|
$timestamp = time();
|
|
$shortPayload = random_bytes(Ksuid::PAYLOAD_BYTES - 1);
|
|
|
|
expect(fn () => Ksuid::fromTimestampAndPayload($timestamp, $shortPayload))
|
|
->toThrow(InvalidArgumentException::class, 'Payload must be exactly 16 bytes');
|
|
});
|
|
|
|
it('validates timestamp before epoch', function () {
|
|
$earlyTimestamp = Ksuid::EPOCH - 1;
|
|
$payload = random_bytes(Ksuid::PAYLOAD_BYTES);
|
|
|
|
expect(fn () => Ksuid::fromTimestampAndPayload($earlyTimestamp, $payload))
|
|
->toThrow(InvalidArgumentException::class, 'Timestamp cannot be before KSUID epoch');
|
|
});
|
|
|
|
it('parses timestamp correctly', function () {
|
|
$expectedTimestamp = 1609459200; // 2021-01-01 00:00:00 UTC
|
|
$payload = random_bytes(Ksuid::PAYLOAD_BYTES);
|
|
|
|
$ksuid = Ksuid::fromTimestampAndPayload($expectedTimestamp, $payload);
|
|
|
|
expect($ksuid->getTimestamp())->toBe($expectedTimestamp);
|
|
});
|
|
|
|
it('gets DateTime from timestamp', function () {
|
|
$timestamp = 1609459200; // 2021-01-01 00:00:00 UTC
|
|
$payload = random_bytes(Ksuid::PAYLOAD_BYTES);
|
|
|
|
$ksuid = Ksuid::fromTimestampAndPayload($timestamp, $payload);
|
|
$dateTime = $ksuid->getDateTime();
|
|
|
|
expect($dateTime->getTimestamp())->toBe($timestamp);
|
|
expect($dateTime->format('Y-m-d H:i:s'))->toBe('2021-01-01 00:00:00');
|
|
});
|
|
|
|
it('checks equality between KSUIDs', function () {
|
|
$value = '2SwcbqZrBNGd67ZJYmPKx42wKZj';
|
|
$ksuid1 = Ksuid::fromString($value);
|
|
$ksuid2 = Ksuid::fromString($value);
|
|
$ksuid3 = Ksuid::fromString('2SwcbqZrBNGd67ZJYmPKx42wKZk');
|
|
|
|
expect($ksuid1->equals($ksuid2))->toBeTrue();
|
|
expect($ksuid1->equals($ksuid3))->toBeFalse();
|
|
});
|
|
|
|
it('compares KSUIDs for sorting', function () {
|
|
$value1 = '2SwcbqZrBNGd67ZJYmPKx42wKZj';
|
|
$value2 = '2SwcbqZrBNGd67ZJYmPKx42wKZk'; // Lexicographically greater
|
|
|
|
$ksuid1 = Ksuid::fromString($value1);
|
|
$ksuid2 = Ksuid::fromString($value2);
|
|
|
|
expect($ksuid1->compare($ksuid2))->toBeLessThan(0);
|
|
expect($ksuid2->compare($ksuid1))->toBeGreaterThan(0);
|
|
expect($ksuid1->compare($ksuid1))->toBe(0);
|
|
});
|
|
|
|
it('checks age comparisons', function () {
|
|
$olderTimestamp = time() - 3600; // 1 hour ago
|
|
$newerTimestamp = time();
|
|
|
|
$payload1 = random_bytes(Ksuid::PAYLOAD_BYTES);
|
|
$payload2 = random_bytes(Ksuid::PAYLOAD_BYTES);
|
|
|
|
$olderKsuid = Ksuid::fromTimestampAndPayload($olderTimestamp, $payload1);
|
|
$newerKsuid = Ksuid::fromTimestampAndPayload($newerTimestamp, $payload2);
|
|
|
|
expect($olderKsuid->isOlderThan($newerKsuid))->toBeTrue();
|
|
expect($newerKsuid->isNewerThan($olderKsuid))->toBeTrue();
|
|
expect($olderKsuid->isNewerThan($newerKsuid))->toBeFalse();
|
|
expect($newerKsuid->isOlderThan($olderKsuid))->toBeFalse();
|
|
});
|
|
|
|
it('calculates age in seconds', function () {
|
|
$timestamp = time() - 100; // 100 seconds ago
|
|
$payload = random_bytes(Ksuid::PAYLOAD_BYTES);
|
|
|
|
$ksuid = Ksuid::fromTimestampAndPayload($timestamp, $payload);
|
|
$age = $ksuid->getAgeInSeconds();
|
|
|
|
expect($age)->toBeGreaterThanOrEqual(99);
|
|
expect($age)->toBeLessThanOrEqual(101); // Allow 1 second tolerance
|
|
});
|
|
|
|
it('converts to string using magic method', function () {
|
|
$value = '2SwcbqZrBNGd67ZJYmPKx42wKZj';
|
|
$ksuid = Ksuid::fromString($value);
|
|
|
|
expect((string)$ksuid)->toBe($value);
|
|
});
|
|
|
|
it('handles Base62 encoding/decoding correctly', function () {
|
|
$originalBytes = random_bytes(Ksuid::TOTAL_BYTES);
|
|
$ksuid = Ksuid::fromBytes($originalBytes);
|
|
$decodedBytes = $ksuid->getBytes();
|
|
|
|
expect($decodedBytes)->toBe($originalBytes);
|
|
});
|
|
|
|
it('handles zero timestamp correctly', function () {
|
|
$epochTimestamp = Ksuid::EPOCH; // Exactly at epoch
|
|
$payload = str_repeat("\x00", Ksuid::PAYLOAD_BYTES);
|
|
|
|
$ksuid = Ksuid::fromTimestampAndPayload($epochTimestamp, $payload);
|
|
|
|
expect($ksuid->getTimestamp())->toBe($epochTimestamp);
|
|
|
|
// Should create the minimum possible KSUID
|
|
$expectedEncoded = str_repeat('0', Ksuid::ENCODED_LENGTH);
|
|
expect($ksuid->toString())->toBe($expectedEncoded);
|
|
});
|
|
|
|
it('handles maximum values correctly', function () {
|
|
$maxTimestamp = Ksuid::EPOCH + 0xFFFFFFFF; // Max 32-bit timestamp
|
|
$maxPayload = str_repeat("\xFF", Ksuid::PAYLOAD_BYTES);
|
|
|
|
$ksuid = Ksuid::fromTimestampAndPayload($maxTimestamp, $maxPayload);
|
|
|
|
expect($ksuid->getTimestamp())->toBe($maxTimestamp);
|
|
expect($ksuid->getPayload())->toBe($maxPayload);
|
|
});
|
|
|
|
it('throws exception for empty KSUID', function () {
|
|
expect(fn () => Ksuid::fromString(''))
|
|
->toThrow(InvalidArgumentException::class, 'KSUID cannot be empty');
|
|
});
|
|
|
|
it('throws exception for invalid bytes length', function () {
|
|
expect(fn () => Ksuid::fromBytes('tooshort'))
|
|
->toThrow(InvalidArgumentException::class, 'KSUID bytes must be exactly 20 bytes');
|
|
});
|