diff --git a/src/GetSequence.php b/src/GetSequence.php index b79da56..231a161 100644 --- a/src/GetSequence.php +++ b/src/GetSequence.php @@ -15,19 +15,19 @@ trait GetSequence * Generates a sequence number based on the current time. * * @param int $dateTime The current time. - * @param string $machineId The machine ID. + * @param int $machineId The machine ID. * @param string $type The type identifier. - * @param int $maxSequence The maximum sequence number. + * @param int $maxSequenceLength The maximum length of the sequence number. * @return int The generated sequence number, or `0` if lock was not acquired. */ - private static function sequence(int $dateTime, string $machineId, string $type, int $maxSequence = 0): int + private static function sequence(int $dateTime, int $machineId, string $type, int $maxSequenceLength = 0): int { self::$fileLocation ??= sys_get_temp_dir() . DIRECTORY_SEPARATOR . "uid-$type-$machineId.seq"; // Attempt to acquire a lock $handle = self::acquireLock(); if (!$handle) { - return getmypid() % ($maxSequence + 1); + return getmypid() % ((-1 ^ (-1 << $maxSequenceLength)) + 1); } // Update sequence diff --git a/src/TBSL.php b/src/TBSL.php index 1bd7857..5d94deb 100644 --- a/src/TBSL.php +++ b/src/TBSL.php @@ -31,7 +31,7 @@ public static function generate(int $machineId = 0, bool $sequenced = false): st return strtoupper(sprintf( '%015s%05s', $storeData, - substr(self::sequencedGenerate($machineId, $sequenced, (int)$timeSequence), -1, 5) + substr(self::sequencedGenerate($machineId, $sequenced, (int)$timeSequence), 0, 5) )); } @@ -62,7 +62,7 @@ private static function sequencedGenerate(int $machineId, bool $enableSequence, public static function parse(string $tbsl): array { $data = [ - 'isValid' => preg_match('/^[0-9A-F]{20}$/', $tbsl), + 'isValid' => (bool)preg_match('/^[0-9A-F]{20}$/', $tbsl), 'time' => null, 'machineId' => null, ]; @@ -73,7 +73,7 @@ public static function parse(string $tbsl): array $storeData = base_convert(substr($tbsl, 0, 15), 16, 10); $data['time'] = new DateTimeImmutable('@' . substr($storeData, 0, 10) . '.' . substr($storeData, 10, 6)); - $data['machineId'] = substr($storeData, -2); + $data['machineId'] = (int)substr($storeData, -2); return $data; } diff --git a/tests/SnowflakeTest.php b/tests/SnowflakeTest.php index 20b848e..0ff3b18 100644 --- a/tests/SnowflakeTest.php +++ b/tests/SnowflakeTest.php @@ -2,11 +2,53 @@ use Infocyph\UID\Snowflake; -test('Basic', function () { +test('Snowflake Basic Functionality', function () { $sf = Snowflake::generate(); $parsed = Snowflake::parse($sf); + expect($parsed['time']->getTimestamp())->toBeBetween(time() - 1, time()) ->and($parsed['worker_id'])->toBe(0) ->and($parsed['datacenter_id'])->toBe(0); }); +test('Snowflake ID Uniqueness', function () { + $id1 = Snowflake::generate(); + usleep(10); + $id2 = Snowflake::generate(); + + expect($id1)->not->toBe($id2); +}); + +test('Snowflake Sequential Order', function () { + $id1 = Snowflake::generate(); + usleep(10); + $id2 = Snowflake::generate(); + + expect((int) $id2)->toBeGreaterThan((int) $id1); +}); + +test('Snowflake Datacenter and Worker Differentiation', function () { + $id1 = Snowflake::generate(1, 1); + $id2 = Snowflake::generate(2, 2); + + $parsed1 = Snowflake::parse($id1); + $parsed2 = Snowflake::parse($id2); + + expect($parsed1['worker_id'])->not->toBe($parsed2['worker_id']) + ->and($parsed1['datacenter_id'])->not->toBe($parsed2['datacenter_id']); +}); + +test('Snowflake Max Sequence Handling', function () { + $maxSeq = (-1 ^ (-1 << 12)); + + $id1 = Snowflake::generate(); + for ($i = 0; $i <= $maxSeq; $i++) { + $id1 = Snowflake::generate(); + } + usleep(10); + $id2 = Snowflake::generate(); + + expect((int) $id2)->toBeGreaterThan((int) $id1); +}); + + diff --git a/tests/SonyflakeTest.php b/tests/SonyflakeTest.php index ede1091..0734047 100644 --- a/tests/SonyflakeTest.php +++ b/tests/SonyflakeTest.php @@ -2,10 +2,50 @@ use Infocyph\UID\Sonyflake; -test('Basic', function () { +test('Sonyflake Basic Functionality', function () { $sf = Sonyflake::generate(); $parsed = Sonyflake::parse($sf); + expect($parsed['time']->getTimestamp())->toBeBetween(time() - 1, time()) ->and($parsed['machine_id'])->toBe(0); }); +test('Sonyflake ID Uniqueness', function () { + $id1 = Sonyflake::generate(); + usleep(10); + $id2 = Sonyflake::generate(); + + expect($id1)->not->toBe($id2); +}); + +test('Sonyflake Sequential Order', function () { + $id1 = Sonyflake::generate(); + usleep(10); + $id2 = Sonyflake::generate(); + + expect((int) $id2)->toBeGreaterThan((int) $id1); +}); + +test('Sonyflake Machine ID Differentiation', function () { + $id1 = Sonyflake::generate(1); + $id2 = Sonyflake::generate(2); + + $parsed1 = Sonyflake::parse($id1); + $parsed2 = Sonyflake::parse($id2); + + expect($parsed1['machine_id'])->not->toBe($parsed2['machine_id']); +}); + +test('Sonyflake Max Sequence Handling', function () { + $maxSeq = (-1 ^ (-1 << 8)); + + $id1 = Sonyflake::generate(); + for ($i = 0; $i <= $maxSeq; $i++) { + $id1 = Sonyflake::generate(); + } + usleep(10); + $id2 = Sonyflake::generate(); + + expect((int) $id2)->toBeGreaterThan((int) $id1); +}); + diff --git a/tests/TBSLTest.php b/tests/TBSLTest.php index d5aeb1f..8441a60 100644 --- a/tests/TBSLTest.php +++ b/tests/TBSLTest.php @@ -2,10 +2,37 @@ use Infocyph\UID\TBSL; -test('Basic', function () { +test('TBSL Basic Functionality', function () { $sf = TBSL::generate(); $parsed = TBSL::parse($sf); - expect($parsed['time']->getTimestamp())->toBeBetween(time() - 1, time()) - ->and($parsed['machineId'])->toBe('00'); + expect($parsed['isValid'])->toBeTrue() + ->and($parsed['time']->getTimestamp())->toBeBetween(time() - 1, time()) + ->and($parsed['machineId'])->toBe(0); +}); + +test('TBSL ID Uniqueness', function () { + $id1 = TBSL::generate(); + usleep(10); // Ensure slight time difference + $id2 = TBSL::generate(); + + expect($id1)->not->toBe($id2); +}); + +test('TBSL Sequential Order', function () { + $id1 = TBSL::generate(); + usleep(10); + $id2 = TBSL::generate(); + + expect(hexdec($id2))->toBeGreaterThan(hexdec($id1)); +}); + +test('TBSL Machine ID Differentiation', function () { + $id1 = TBSL::generate(1); + $id2 = TBSL::generate(2); + + $parsed1 = TBSL::parse($id1); + $parsed2 = TBSL::parse($id2); + + expect($parsed1['machineId'])->not->toBe($parsed2['machineId']); });