Skip to content

Commit

Permalink
Improve tests
Browse files Browse the repository at this point in the history
  • Loading branch information
paulbalandan committed Dec 21, 2024
1 parent 8c50a19 commit 3b60618
Show file tree
Hide file tree
Showing 8 changed files with 111 additions and 32 deletions.
3 changes: 3 additions & 0 deletions infection.json5
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@
"minMsi": 100,
"minCoveredMsi": 100,
"mutators": {
"global-ignore": [
"Nexus\\Password\\Hash\\*::isValidPassword"
],
"ArrayAll": true,
"ArrayAny": true,
"ArrayItem": true,
Expand Down
69 changes: 48 additions & 21 deletions tests/Password/Hash/AbstractArgon2HashTestCase.php
Original file line number Diff line number Diff line change
Expand Up @@ -28,29 +28,29 @@ public function testInvalidMemoryCost(): void
$this->expectException(HashException::class);
$this->expectExceptionMessage('Memory cost should be 7KiB or greater, 5KiB given.');

$this->argonHash(['memory_cost' => 5 * 1024]);
static::argonHash(['memory_cost' => 5 * 1024]);
}

public function testInvalidTimeCost(): void
{
$this->expectException(HashException::class);
$this->expectExceptionMessage('Time cost should be 1 or greater, 0 given.');

$this->argonHash(['time_cost' => 0]);
static::argonHash(['time_cost' => 0]);
}

public function testInvalidThreads(): void
{
$this->expectException(HashException::class);
$this->expectExceptionMessage('Number of threads should be 1 or greater, 0 given.');

$this->argonHash(['threads' => 0]);
static::argonHash(['threads' => 0]);
}

public function testBasicPasswordHashing(): void
{
$password = 'my-awesome-password';
$hasher = $this->argonHash();
$hasher = static::argonHash();

$hash = $hasher->hash($password);
self::assertTrue($hasher->verify($password, $hash));
Expand All @@ -67,7 +67,7 @@ public function testBasicPasswordHashing(): void
public function testPasswordNeedsRehash(array $option): void
{
$password = 'myPassword';
$hasher = $this->argonHash();
$hasher = static::argonHash();

$hash = $hasher->hash($password);
self::assertFalse($hasher->needsRehash($hash));
Expand All @@ -86,32 +86,59 @@ public static function providePasswordNeedsRehashCases(): iterable
yield 'threads' => [['threads' => 3]];
}

public function testInvalidPasswordPassedToHash(): void
#[DataProvider('provideInvalidPasswordForHashCases')]
public function testInvalidPasswordPassedToHash(string $password): void
{
$this->expectException(HashException::class);
$this->expectExceptionMessage('Invalid password provided.');

$this->argonHash()->hash("pass\0word");
static::argonHash()->hash($password);
}

public function testInvalidPasswordForVerify(): void
/**
* @return iterable<string, array{string}>
*/
public static function provideInvalidPasswordForHashCases(): iterable
{
yield 'empty' => [''];

yield 'nul' => ["pass\0word"];

yield 'short' => ['pass'];

yield 'very long' => [str_repeat('a', 4098)];
}

#[DataProvider('provideInvalidPasswordForVerifyCases')]
public function testInvalidPasswordForVerify(bool $result, ?string $password, string $hash): void
{
$pass1 = "abcd\0e";
$pass2 = str_repeat('a', 4098);
$pass3 = 'password';
$pass4 = 'pass';
$hasher = $this->argonHash();

$hash = $hasher->hash($pass3);
self::assertFalse($hasher->verify($pass1, $hash));
self::assertFalse($hasher->verify($pass2, $hash));
self::assertTrue($hasher->verify($pass3, $hash));
self::assertFalse($hasher->verify($pass4, $hash));
$password ??= 'password';
$hasher = static::argonHash();

self::assertSame($result, $hasher->verify($password, $hash));
}

/**
* @return iterable<string, array{bool, null|string, string}>
*/
public static function provideInvalidPasswordForVerifyCases(): iterable
{
$hash = static::argonHash()->hash('password');

yield 'empty' => [false, '', $hash];

yield 'nul' => [false, "pass\0word", $hash];

yield 'short' => [false, 'aa', $hash];

yield 'very long' => [false, str_repeat('a', 4098), $hash];

yield 'valid hash' => [true, null, $hash];
}

public function testIsValidHash(): void
{
self::assertTrue($this->argonHash()->valid());
self::assertTrue(static::argonHash()->valid());
}

/**
Expand All @@ -121,5 +148,5 @@ public function testIsValidHash(): void
* threads?: int,
* } $options
*/
abstract protected function argonHash(array $options = []): AbstractArgon2Hash;
abstract protected static function argonHash(array $options = []): AbstractArgon2Hash;
}
2 changes: 1 addition & 1 deletion tests/Password/Hash/Argon2iHashTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ public function testInvalidAlgorithm(): void
new Argon2iHash(Algorithm::Argon2id);
}

protected function argonHash(array $options = []): AbstractArgon2Hash
protected static function argonHash(array $options = []): AbstractArgon2Hash
{
return new Argon2iHash(Algorithm::Argon2i, $options);
}
Expand Down
2 changes: 1 addition & 1 deletion tests/Password/Hash/Argon2idHashTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ public function testInvalidAlgorithm(): void
new Argon2idHash(Algorithm::Argon2i);
}

protected function argonHash(array $options = []): AbstractArgon2Hash
protected static function argonHash(array $options = []): AbstractArgon2Hash
{
return new Argon2idHash(Algorithm::Argon2id, $options);
}
Expand Down
10 changes: 9 additions & 1 deletion tests/Password/Hash/BcryptHashTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -93,9 +93,15 @@ public function testInvalidPasswordPassedToBcrypt(string $password): void
*/
public static function provideInvalidPasswordPassedToBcryptCases(): iterable
{
yield 'invalid' => ["pass\0word"];
yield 'empty' => [''];

yield 'nul' => ["pass\0word"];

yield 'short' => ['pass'];

yield 'long' => [str_repeat('a', 75)];

yield 'very long' => [str_repeat('a', 4098)];
}

public function testLongPasswordNearingMaxWorks(): void
Expand Down Expand Up @@ -131,6 +137,8 @@ public static function provideInvalidPasswordForVerifyCases(): iterable

yield 'bcrypt max' => [false, str_repeat('a', 75), $hash];

yield 'very long' => [false, str_repeat('a', 4098), $hash];

yield 'corrupted' => [false, null, str_replace('$2y', '$3y', $hash)];

yield 'valid hash' => [true, null, $hash];
Expand Down
21 changes: 19 additions & 2 deletions tests/Password/Hash/Pbkdf2HashTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -89,12 +89,27 @@ public function testBasicPasswordHashing(): void
self::assertFalse($hasher->needsRehash($hash));
}

public function testInvalidPasswordForHash(): void
#[DataProvider('provideInvalidPasswordForHashCases')]
public function testInvalidPasswordForHash(string $password): void
{
$this->expectException(HashException::class);
$this->expectExceptionMessage('Invalid password provided.');

(new Pbkdf2Hash(Algorithm::Pbkdf2HmacSha256))->hash('aa');
(new Pbkdf2Hash(Algorithm::Pbkdf2HmacSha256))->hash($password);
}

/**
* @return iterable<string, array{string}>
*/
public static function provideInvalidPasswordForHashCases(): iterable
{
yield 'empty' => [''];

yield 'nul' => ["pass\0word"];

yield 'short' => ['pass'];

yield 'very long' => [str_repeat('a', 4098)];
}

#[DataProvider('providePasswordVerifyCases')]
Expand All @@ -120,6 +135,8 @@ public static function providePasswordVerifyCases(): iterable

yield 'short' => [false, 'pass', $hash];

yield 'very long' => [false, str_repeat('a', 4098), $hash];

yield 'not hash' => [false, null, 'hash'];

yield 'truncated' => [false, null, substr($hash, 7)];
Expand Down
21 changes: 19 additions & 2 deletions tests/Password/Hash/SodiumHashTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -65,12 +65,27 @@ public function testBasicPasswordHashing(): void
self::assertFalse($hasher->needsRehash($hash));
}

public function testInvalidPasswordForHash(): void
#[DataProvider('provideInvalidPasswordForHashCases')]
public function testInvalidPasswordForHash(string $password): void
{
$this->expectException(HashException::class);
$this->expectExceptionMessage('Invalid password provided.');

(new SodiumHash(Algorithm::Sodium))->hash('aa');
(new SodiumHash(Algorithm::Sodium))->hash($password);
}

/**
* @return iterable<string, array{string}>
*/
public static function provideInvalidPasswordForHashCases(): iterable
{
yield 'empty' => [''];

yield 'nul' => ["pass\0word"];

yield 'short' => ['pass'];

yield 'very long' => [str_repeat('a', 4098)];
}

public function testNeedsRehashing(): void
Expand Down Expand Up @@ -105,6 +120,8 @@ public static function provideInvalidPasswordForVerifyCases(): iterable

yield 'short' => [false, 'aa', $hash];

yield 'very long' => [false, str_repeat('a', 4098), $hash];

yield 'corrupted' => [false, null, str_replace(SODIUM_CRYPTO_PWHASH_STRPREFIX, '$2y$', $hash)];

yield 'valid hash' => [true, null, $hash];
Expand Down
15 changes: 11 additions & 4 deletions tools/src/InfectionConfigBuilder.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
use Infection\Mutator\ProfileList;
use Nexus\Clock\SystemClock;
use Nexus\Collection\Collection;
use Nexus\Password\Hash\AbstractHash;
use Nexus\Password\Hash\SodiumHash;

/**
Expand All @@ -25,10 +26,14 @@
*/
final class InfectionConfigBuilder
{
public const array GLOBAL_IGNORE = [
'Nexus\\Password\\Hash\\*::isValidPassword',
];

/**
* @var list<string>
*/
public const UNWANTED_MUTATORS = [
public const array UNWANTED_MUTATORS = [
'Concat',
'DecrementInteger',
'FunctionCallRemoval',
Expand All @@ -54,7 +59,7 @@ final class InfectionConfigBuilder
*
* @var array<string, list<string>>
*/
public const PER_MUTATOR_IGNORE = [
public const array PER_MUTATOR_IGNORE = [
'CastBool' => [
Collection::class.'::filterWithKey',
Collection::class.'::reject',
Expand Down Expand Up @@ -83,7 +88,7 @@ final class InfectionConfigBuilder
* 'tmpDir': string,
* 'minMsi': int,
* 'minCoveredMsi': int,
* 'mutators': array<string, bool|array<string, mixed>>,
* 'mutators': array<string, bool|array<string, mixed>|list<string>>,
* 'testFramework': string,
* 'testFrameworkOptions': string,
* }
Expand All @@ -105,7 +110,7 @@ public static function build(): array
'tmpDir' => 'build',
'minMsi' => 100,
'minCoveredMsi' => 100,
'mutators' => [],
'mutators' => ['global-ignore' => []],
'testFramework' => 'phpunit',
'testFrameworkOptions' => '--group=unit-test',
];
Expand All @@ -127,6 +132,8 @@ public static function build(): array
$config['mutators'][$mutator] = true;
}

$config['mutators']['global-ignore'] = self::GLOBAL_IGNORE;

return $config;
}
}

0 comments on commit 3b60618

Please sign in to comment.