Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[10.x] Add ulid testing helpers #48276

Merged
merged 3 commits into from Sep 4, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
89 changes: 89 additions & 0 deletions src/Illuminate/Support/Str.php
Expand Up @@ -50,6 +50,13 @@ class Str
*/
protected static $uuidFactory;

/**
* The callback that should be used to generate ULIDs.
*
* @var callable|null
*/
protected static $ulidFactory;

/**
* The callback that should be used to generate random strings.
*
Expand Down Expand Up @@ -1543,13 +1550,95 @@ public static function createUuidsNormally()
*/
public static function ulid($time = null)
{
if (static::$ulidFactory) {
return call_user_func(static::$ulidFactory);
}

if ($time === null) {
return new Ulid();
}

return new Ulid(Ulid::generate($time));
}

/**
* Indicate that ULIDs should be created normally and not using a custom factory.
*
* @return void
*/
public static function createUlidsNormally()
{
static::$ulidFactory = null;
}

/**
* Set the callable that will be used to generate ULIDs.
*
* @param callable|null $factory
* @return void
*/
public static function createUlidsUsing(callable $factory = null)
{
static::$ulidFactory = $factory;
}

/**
* Set the sequence that will be used to generate ULIDs.
*
* @param array $sequence
* @param callable|null $whenMissing
* @return void
*/
public static function createUlidsUsingSequence(array $sequence, $whenMissing = null)
{
$next = 0;

$whenMissing ??= function () use (&$next) {
$factoryCache = static::$ulidFactory;

static::$ulidFactory = null;

$ulid = static::ulid();

static::$ulidFactory = $factoryCache;

$next++;

return $ulid;
};

static::createUlidsUsing(function () use (&$next, $sequence, $whenMissing) {
if (array_key_exists($next, $sequence)) {
return $sequence[$next++];
}

return $whenMissing();
});
}

/**
* Always return the same ULID when generating new ULIDs.
*
* @param Closure|null $callback
* @return Ulid
*/
public static function freezeUlids(Closure $callback = null)
{
$ulid = Str::ulid();

Str::createUlidsUsing(fn () => $ulid);

if ($callback !== null) {
try {
$callback($ulid);
} finally {
Str::createUlidsNormally();
}
}

return $ulid;
}

/**
* Remove all strings from the casing caches.
*
Expand Down
105 changes: 105 additions & 0 deletions tests/Support/SupportStrTest.php
Expand Up @@ -1155,6 +1155,111 @@ public function testItCanSpecifyAFallbackForASequence()
}
}

public function testItCanFreezeUlids()
{
$this->assertNotSame((string) Str::ulid(), (string) Str::ulid());
$this->assertNotSame(Str::ulid(), Str::ulid());

$ulid = Str::freezeUlids();

$this->assertSame($ulid, Str::ulid());
$this->assertSame(Str::ulid(), Str::ulid());
$this->assertSame((string) $ulid, (string) Str::ulid());
$this->assertSame((string) Str::ulid(), (string) Str::ulid());

Str::createUlidsNormally();

$this->assertNotSame(Str::ulid(), Str::ulid());
$this->assertNotSame((string) Str::ulid(), (string) Str::ulid());
}

public function testItCanFreezeUlidsInAClosure()
{
$ulids = [];

$ulid = Str::freezeUlids(function ($ulid) use (&$ulids) {
$ulids[] = $ulid;
$ulids[] = Str::ulid();
$ulids[] = Str::ulid();
});

$this->assertSame($ulid, $ulids[0]);
$this->assertSame((string) $ulid, (string) $ulids[0]);
$this->assertSame((string) $ulids[0], (string) $ulids[1]);
$this->assertSame($ulids[0], $ulids[1]);
$this->assertSame((string) $ulids[0], (string) $ulids[1]);
$this->assertSame($ulids[1], $ulids[2]);
$this->assertSame((string) $ulids[1], (string) $ulids[2]);
$this->assertNotSame(Str::ulid(), Str::ulid());
$this->assertNotSame((string) Str::ulid(), (string) Str::ulid());

Str::createUlidsNormally();
}

public function testItCreatesUlidsNormallyAfterFailureWithinFreezeMethod()
{
try {
Str::freezeUlids(function () {
Str::createUlidsUsing(fn () => Str::of('1234'));
$this->assertSame('1234', (string) Str::ulid());
throw new \Exception('Something failed');
});
} catch (\Exception) {
$this->assertNotSame('1234', (string) Str::ulid());
}
}

public function testItCanSpecifyASequenceOfUlidsToUtilise()
{
Str::createUlidsUsingSequence([
0 => ($zeroth = Str::ulid()),
1 => ($first = Str::ulid()),
// just generate a random one here...
3 => ($third = Str::ulid()),
// continue to generate random ulids...
]);

$retrieved = Str::ulid();
$this->assertSame($zeroth, $retrieved);
$this->assertSame((string) $zeroth, (string) $retrieved);

$retrieved = Str::ulid();
$this->assertSame($first, $retrieved);
$this->assertSame((string) $first, (string) $retrieved);

$retrieved = Str::ulid();
$this->assertFalse(in_array($retrieved, [$zeroth, $first, $third], true));
$this->assertFalse(in_array((string) $retrieved, [(string) $zeroth, (string) $first, (string) $third], true));

$retrieved = Str::ulid();
$this->assertSame($third, $retrieved);
$this->assertSame((string) $third, (string) $retrieved);

$retrieved = Str::ulid();
$this->assertFalse(in_array($retrieved, [$zeroth, $first, $third], true));
$this->assertFalse(in_array((string) $retrieved, [(string) $zeroth, (string) $first, (string) $third], true));

Str::createUlidsNormally();
}

public function testItCanSpecifyAFallbackForAUlidSequence()
{
Str::createUlidsUsingSequence(
[Str::ulid(), Str::ulid()],
fn () => throw new Exception('Out of Ulids'),
);
Str::ulid();
Str::ulid();

try {
$this->expectExceptionMessage('Out of Ulids');
Str::ulid();
$this->fail();
} finally {
Str::createUlidsNormally();
}
}

public function testPasswordCreation()
{
$this->assertTrue(strlen(Str::password()) === 32);
Expand Down