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

[1.x] Unit tests #18

Open
wants to merge 7 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
},
"require-dev": {
"friendsofphp/php-cs-fixer": "^3.26",
"mockery/mockery": "^1.6",
"nunomaduro/larastan": "^2.0",
"orchestra/testbench": "^7.0|^8.0",
"pestphp/pest": "^1.23|^2.18",
Expand Down
3 changes: 3 additions & 0 deletions src/AuthResult.php
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,9 @@ public function getUser(): ?AuthenticatableInterface

public function throwIfError(): self
{
/**
* @phpstan-ignore-next-line
*/
return ! $this->isSuccessful() ? throw $this->getException() : $this;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,6 @@ public function getFailedAttemptsCount(OneTimePasswordEntityInterface $entity):

public function incrementFailAttemptsCount(OneTimePasswordEntityInterface $entity, int $value = 1): int
{
/**
* @phpstan-ignore-next-line
*/
$value = $this->connection->incr($key = self::getKey($entity->getKey()), $value);

$this->connection->expire($key, (int)$entity->getValidInterval()->addDays(1)->totalSeconds);
Expand Down
16 changes: 2 additions & 14 deletions src/Infrastructure/OneTimePassword/Support/CodeGenerator.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,27 +2,15 @@

namespace LaravelAuthPro\Infrastructure\OneTimePassword\Support;

use Illuminate\Contracts\Config\Repository;
use Illuminate\Support\Str;
use LaravelAuthPro\Contracts\Base\GeneratorInterface;
use LaravelAuthPro\Infrastructure\OneTimePassword\Enum\OneTimePasswordCodeType;

class CodeGenerator implements GeneratorInterface
{
protected readonly int $length;
protected readonly OneTimePasswordCodeType $type;

public function __construct(Repository $configRepository)
public function __construct(protected readonly OneTimePasswordCodeType $type = OneTimePasswordCodeType::DIGIT, protected readonly int $length = 6)
{
/**
* @phpstan-ignore-next-line
*/
$this->length = $configRepository->get('auth_pro.one_time_password.code.length', 6);

/**
* @phpstan-ignore-next-line
*/
$this->type = $configRepository->get('auth_pro.one_time_password.code.type', OneTimePasswordCodeType::DIGIT);
//
}

public function generate(int $length = null): string
Expand Down
24 changes: 10 additions & 14 deletions src/Infrastructure/OneTimePassword/Support/TokenGenerator.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,27 +2,15 @@

namespace LaravelAuthPro\Infrastructure\OneTimePassword\Support;

use Illuminate\Contracts\Config\Repository;
use Illuminate\Support\Str;
use LaravelAuthPro\Contracts\Base\GeneratorInterface;
use LaravelAuthPro\Infrastructure\OneTimePassword\Enum\OneTimePasswordTokenType;

class TokenGenerator implements GeneratorInterface
{
protected readonly int $length;
protected readonly OneTimePasswordTokenType $type;

public function __construct(Repository $configRepository)
public function __construct(protected readonly OneTimePasswordTokenType $type = OneTimePasswordTokenType::RANDOM_STRING, protected readonly int $length = 8)
{
/**
* @phpstan-ignore-next-line
*/
$this->length = $configRepository->get('one_time_password.token.length', 8);

/**
* @phpstan-ignore-next-line
*/
$this->type = OneTimePasswordTokenType::from($configRepository->get('one_time_password.token.type', 'random_string'));
//
}

/**
Expand All @@ -42,6 +30,14 @@ public function generate(int $length = null): string

private function generateRandomInt(int $length): int
{
if ($length > 18) {
// It's reached PHP_MAX_INT value and will convert to float, but random_int method accept int as min and max.
throw new \RuntimeException('$length is too large. Length should not above 18');
}

/**
* @phpstan-ignore-next-line
*/
return random_int(10 ** ($length - 1), (10 ** $length) - 1);
}
}
2 changes: 1 addition & 1 deletion src/Traits/HasPayload.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ trait HasPayload
private function fillAttributes(array $payload): void
{
$staticProperties = array_keys(get_class_vars(static::class));
$staticProperties = array_diff($staticProperties, array_keys(get_object_vars($this)));
// $staticProperties = array_diff($staticProperties, array_keys(get_object_vars($this)));

foreach ($staticProperties as $property) {
if (! empty($payload[$property])) {
Expand Down
20 changes: 20 additions & 0 deletions tests/Unit/Base/BaseServiceTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<?php

describe('test abstract base service', function () {
it('throw if repository not provided', function () {
$repository = Mockery::mock(\LaravelAuthPro\Base\BaseRepository::class);

$class = new class () extends \LaravelAuthPro\Base\BaseService {
//
};

$class = new $class($repository);
expect($class->hasRepository())->toBeTrue();

$class = new $class();
expect($class->hasRepository())
->toBeFalse()
->and(fn () => $class->throwIfRepositoryNotProvided())
->toThrow(\InvalidArgumentException::class);
});
});
42 changes: 42 additions & 0 deletions tests/Unit/Contracts/Exceptions/AuthExceptionTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
<?php

describe('test auth exception', function () {
it('create new instance', function () {
$e = new \LaravelAuthPro\Contracts\Exceptions\AuthException('my_error', 500, ['foo' => 'bar']);

$translatorMock = Mockery::mock(\Illuminate\Translation\Translator::class)
->shouldReceive('get')
->andReturn('My Error')
->getMock();

$reposeFactory = Mockery::mock($rf = \Illuminate\Contracts\Routing\ResponseFactory::class)
->shouldReceive('make')
->andReturn(new \Illuminate\Http\Response())
->getMock();

app()['translator'] = $translatorMock;
app()[$rf] = $reposeFactory;

expect($e->getErrorMessage())
->toEqual('my_error')
->and($e->getCode())
->toEqual(500)
->and($e->report())
->toBeFalse()
->and($e->render(new \Illuminate\Http\Request()))
->toBeInstanceOf(\Illuminate\Http\Response::class);
});

it('custom render function', function () {
$e = new \LaravelAuthPro\Contracts\Exceptions\AuthException('my_error', 500, ['foo' => 'bar']);

\LaravelAuthPro\Contracts\Exceptions\AuthException::setRenderClosure(function (\LaravelAuthPro\Contracts\Exceptions\AuthException $exception) use ($e) {
expect($exception)->toBe($e);

return ['error' => $e->getErrorMessage(), 'code' => $e->getCode()];
});

expect($e->render(new \Illuminate\Http\Request()))
->toBe(['error' => 'my_error', 'code' => 500]);
});
});
48 changes: 48 additions & 0 deletions tests/Unit/Credential/AuthCredentialTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
<?php

beforeAll(function () {
\LaravelAuthPro\AuthPro::shouldReceive('getCredentialsMapper')->andReturn([
\LaravelAuthPro\Contracts\Providers\EmailProviderInterface::class => \LaravelAuthPro\Contracts\Credentials\EmailCredentialInterface::class,
\LaravelAuthPro\Contracts\Providers\PhoneProviderInterface::class => \LaravelAuthPro\Contracts\Credentials\PhoneCredentialInterface::class,
]);

\LaravelAuthPro\AuthPro::shouldReceive('getAuthProvidersMapper')->andReturn([
\LaravelAuthPro\Contracts\Providers\EmailProviderInterface::class => \LaravelAuthPro\Providers\EmailProvider::class,
\LaravelAuthPro\Contracts\Providers\PhoneProviderInterface::class => \LaravelAuthPro\Providers\PhoneProvider::class,
]);
});

describe('test auth credential model class', function () {
it('throw if identifier type not supported', function () {
$identifier = Mockery::mock(\LaravelAuthPro\Contracts\AuthIdentifierInterface::class)
->shouldReceive('getIdentifierType')->andReturn(\LaravelAuthPro\Enums\AuthIdentifierType::EMAIL)
->getMock();

app()->bind(\LaravelAuthPro\Contracts\Credentials\PhoneCredentialInterface::class, \LaravelAuthPro\Credentials\PhoneCredential::class);

(new \LaravelAuthPro\Credentials\Builder\AuthCredentialBuilder())
->with('phone')
->as($identifier)
->by(\LaravelAuthPro\Enums\AuthProviderSignInMethod::PASSWORD)
->withPayload()
->build();
})->throws(\InvalidArgumentException::class, 'Invalid identifier type [EMAIL] in PhoneCredential');

it('payload rule must be available according to base interface parents', function () {
expect(\LaravelAuthPro\Credentials\EmailCredential::class)->toImplement(\LaravelAuthPro\Contracts\Credentials\EmailCredentialInterface::class);

expect(\LaravelAuthPro\Contracts\Credentials\EmailCredentialInterface::class)->toExtend(\LaravelAuthPro\Contracts\Credentials\Base\HasPasswordInterface::class)
->and(\LaravelAuthPro\Credentials\EmailCredential::class)->toExtend(\LaravelAuthPro\Contracts\Credentials\Base\HasOneTimePasswordInterface::class);

expect(\LaravelAuthPro\Credentials\EmailCredential::getPayloadRules())
->toHaveKeys(['password', 'code', 'token']);

expect(\LaravelAuthPro\Credentials\PhoneCredential::getPayloadRules())
->toHaveKeys(['password', 'code', 'token']);
});

it('return correct builder class', function () {
expect(\LaravelAuthPro\Credentials\EmailCredential::getBuilder())
->toBeInstanceOf(\LaravelAuthPro\Credentials\Builder\AuthCredentialBuilder::class);
});
});
66 changes: 66 additions & 0 deletions tests/Unit/Credential/Builder/AuthCredentialBuilderTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
<?php

beforeAll(function () {
\LaravelAuthPro\AuthPro::shouldReceive('getCredentialsMapper')->andReturn([
\LaravelAuthPro\Contracts\Providers\EmailProviderInterface::class => \LaravelAuthPro\Contracts\Credentials\EmailCredentialInterface::class,
\LaravelAuthPro\Contracts\Providers\PhoneProviderInterface::class => \LaravelAuthPro\Contracts\Credentials\PhoneCredentialInterface::class,
]);

\LaravelAuthPro\AuthPro::shouldReceive('getAuthProvidersMapper')->andReturn([
\LaravelAuthPro\Contracts\Providers\EmailProviderInterface::class => \LaravelAuthPro\Providers\EmailProvider::class,
\LaravelAuthPro\Contracts\Providers\PhoneProviderInterface::class => \LaravelAuthPro\Providers\PhoneProvider::class,
]);
});

describe('test auth credential builder', function () {
it('build email credential class', function () {
$authIdentifier = Mockery::mock(\LaravelAuthPro\Contracts\AuthIdentifierInterface::class)
->shouldReceive('getIdentifierType')->andReturn(\LaravelAuthPro\Enums\AuthIdentifierType::EMAIL)
->getMock();

app()->bind(\LaravelAuthPro\Contracts\Credentials\EmailCredentialInterface::class, \LaravelAuthPro\Credentials\EmailCredential::class);

$model = (new \LaravelAuthPro\Credentials\Builder\AuthCredentialBuilder())
->with('email')
->by(\LaravelAuthPro\Enums\AuthProviderSignInMethod::PASSWORD)
->as($authIdentifier)
->withPayload(['password' => 'foo', 'token' => 'bar', 'code' => 'test'])
->build();

expect($model)->toBeInstanceOf(\LaravelAuthPro\Contracts\Credentials\EmailCredentialInterface::class)
->and($model->getIdentifier())->toBe($authIdentifier)
->and($model->getProviderId())->toBe('email')
->and($model->getSignInMethod())->toBe(\LaravelAuthPro\Enums\AuthProviderSignInMethod::PASSWORD)
->and($model->getPassword())->toBe('foo')
->and($model->getOneTimePasswordToken())->toBe('bar')
->and($model->getOneTimePassword())->toBe('test');
});

it('build phone credential class', function () {
$authIdentifier = Mockery::mock(\LaravelAuthPro\Contracts\AuthIdentifierInterface::class)
->shouldReceive('getIdentifierType')->andReturn(\LaravelAuthPro\Enums\AuthIdentifierType::MOBILE)
->getMock();

app()->bind(\LaravelAuthPro\Contracts\Credentials\PhoneCredentialInterface::class, \LaravelAuthPro\Credentials\PhoneCredential::class);

$model = (new \LaravelAuthPro\Credentials\Builder\AuthCredentialBuilder())
->with('phone')
->by(\LaravelAuthPro\Enums\AuthProviderSignInMethod::PASSWORD)
->as($authIdentifier)
->withPayload(['password' => 'foo', 'token' => 'bar', 'code' => 'test'])
->build();

expect($model)->toBeInstanceOf(\LaravelAuthPro\Contracts\Credentials\PhoneCredentialInterface::class)
->and($model->getIdentifier())->toBe($authIdentifier)
->and($model->getProviderId())->toBe('phone')
->and($model->getSignInMethod())->toBe(\LaravelAuthPro\Enums\AuthProviderSignInMethod::PASSWORD)
->and($model->getPassword())->toBe('foo')
->and($model->getOneTimePasswordToken())->toBe('bar')
->and($model->getOneTimePassword())->toBe('test');
});

it('throw error when provider is null', function () {
(new \LaravelAuthPro\Credentials\Builder\AuthCredentialBuilder())
->build();
})->throws(\InvalidArgumentException::class);
});
36 changes: 36 additions & 0 deletions tests/Unit/Credential/EmailCredentialTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
<?php

beforeAll(function () {
app()->flush();

\LaravelAuthPro\AuthPro::shouldReceive('getCredentialsMapper')->andReturn([
\LaravelAuthPro\Contracts\Providers\EmailProviderInterface::class => \LaravelAuthPro\Contracts\Credentials\EmailCredentialInterface::class,
\LaravelAuthPro\Contracts\Providers\PhoneProviderInterface::class => \LaravelAuthPro\Contracts\Credentials\PhoneCredentialInterface::class,
]);

\LaravelAuthPro\AuthPro::shouldReceive('getAuthProvidersMapper')->andReturn([
\LaravelAuthPro\Contracts\Providers\EmailProviderInterface::class => \LaravelAuthPro\Providers\EmailProvider::class,
\LaravelAuthPro\Contracts\Providers\PhoneProviderInterface::class => \LaravelAuthPro\Providers\PhoneProvider::class,
]);
});

describe('test email credential', function () {
it('have correct email according to identifier', function () {
$identifier = Mockery::mock(\LaravelAuthPro\Contracts\AuthIdentifierInterface::class)
->shouldReceive('getIdentifierValue')->andReturn('[email protected]')
->shouldReceive('getIdentifierType')->andReturn(\LaravelAuthPro\Enums\AuthIdentifierType::EMAIL)
->getMock();

app()->bind(\LaravelAuthPro\Contracts\Credentials\EmailCredentialInterface::class, \LaravelAuthPro\Credentials\EmailCredential::class);

$credential = (new \LaravelAuthPro\Credentials\Builder\AuthCredentialBuilder())
->with('email')
->as($identifier)
->by(\LaravelAuthPro\Enums\AuthProviderSignInMethod::PASSWORD)
->withPayload()
->build();

expect($credential)->toBeInstanceOf(\LaravelAuthPro\Credentials\EmailCredential::class)
->and($credential->getEmail())->toBe('[email protected]');
});
});
59 changes: 59 additions & 0 deletions tests/Unit/Credential/PhoneCredentialTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
<?php

beforeAll(function () {
//TODO: Review this later
app()->flush();

\LaravelAuthPro\AuthPro::shouldReceive('getCredentialsMapper')->andReturn([
\LaravelAuthPro\Contracts\Providers\EmailProviderInterface::class => \LaravelAuthPro\Contracts\Credentials\EmailCredentialInterface::class,
\LaravelAuthPro\Contracts\Providers\PhoneProviderInterface::class => \LaravelAuthPro\Contracts\Credentials\PhoneCredentialInterface::class,
]);

\LaravelAuthPro\AuthPro::shouldReceive('getAuthProvidersMapper')->andReturn([
\LaravelAuthPro\Contracts\Providers\EmailProviderInterface::class => \LaravelAuthPro\Providers\EmailProvider::class,
\LaravelAuthPro\Contracts\Providers\PhoneProviderInterface::class => \LaravelAuthPro\Providers\PhoneProvider::class,
]);
});

describe('test phone credential', function () {
it('have correct phone according to identifier', function () {
$authIdentifier = Mockery::mock(\LaravelAuthPro\Contracts\AuthIdentifierInterface::class)
->shouldReceive('getIdentifierValue')->andReturn('111222333')
->shouldReceive('getIdentifierType')->andReturn(\LaravelAuthPro\Enums\AuthIdentifierType::MOBILE)
->getMock();

app()->bind(\LaravelAuthPro\Contracts\Credentials\PhoneCredentialInterface::class, \LaravelAuthPro\Credentials\PhoneCredential::class);

$credential = (new \LaravelAuthPro\Credentials\Builder\AuthCredentialBuilder())
->with('phone')
->by(\LaravelAuthPro\Enums\AuthProviderSignInMethod::PASSWORD)
->as($authIdentifier)
->withPayload(['password' => 'foo', 'token' => 'bar', 'code' => 'test'])
->build();

expect($credential)->toBeInstanceOf(\LaravelAuthPro\Credentials\PhoneCredential::class)
->and($credential->getPhone())->toBe('111222333');
});

it('load signature from encrypted string', function () {
$signature = new \LaravelAuthPro\AuthSignature('id', 'ip', 'user_id', now());

$authIdentifier = Mockery::mock(\LaravelAuthPro\Contracts\AuthIdentifierInterface::class)
->shouldReceive('getIdentifierValue')->andReturn('111222333')
->shouldReceive('getIdentifierType')->andReturn(\LaravelAuthPro\Enums\AuthIdentifierType::MOBILE)
->getMock();


\Illuminate\Support\Facades\Crypt::shouldReceive('encrypt')->andReturn('encrypted_string');
\Illuminate\Support\Facades\Crypt::shouldReceive('decrypt')->andReturn($signature->toArray());

$credential = (new \LaravelAuthPro\Credentials\Builder\AuthCredentialBuilder())
->with('phone')
->by(\LaravelAuthPro\Enums\AuthProviderSignInMethod::PASSWORD)
->as($authIdentifier)
->withPayload(['signature' => $signature->__toString()])
->build();

expect($credential->getSignature()->toArray())->toBe($signature->toArray());
});
});
5 changes: 0 additions & 5 deletions tests/Unit/ExampleTest.php

This file was deleted.

Loading
Loading