Reorganize and enhance tests

This commit is contained in:
Bubka 2021-11-22 01:09:54 +01:00
parent eea7cec043
commit 45b835bbd5
24 changed files with 1417 additions and 1150 deletions

View File

@ -208,7 +208,9 @@ public function withdraw($ids) : void
Log::info(sprintf('TwoFAccounts #%s withdrawn', implode(',#', $ids)));
}
// @codeCoverageIgnoreStart
else Log::info('No TwoFAccount to withdraw');
// @codeCoverageIgnoreEnd
}
@ -289,8 +291,13 @@ private function mapArrayToDto($array) : TwoFAccountDto
{
$dto = new TwoFAccountDto();
foreach ($array as $key => $value) {
$dto->$key = ! Arr::has($array, $key) ?: $value;
try {
foreach ($array as $key => $value) {
$dto->$key = ! Arr::has($array, $key) ?: $value;
}
}
catch (\TypeError $ex) {
throw new InvalidOtpParameterException($ex->getMessage());
}
return $dto;
@ -457,8 +464,10 @@ private function storeTokenImageAsIcon()
Log::info(sprintf('Icon file %s stored', $newFilename));
}
else {
// @codeCoverageIgnoreStart
Storage::delete($imageFile);
throw new \Exception;
// @codeCoverageIgnoreEnd
}
return $newFilename;

View File

@ -69,6 +69,10 @@
],
"test" : [
"vendor/bin/phpunit"
],
"test-coverage-html" : [
"@putenv XDEBUG_MODE=coverage",
"vendor/bin/phpunit --coverage-html tests/Coverage/"
]
}
}

View File

@ -23,6 +23,9 @@
<testsuite name="Feature">
<directory suffix="Test.php">./tests/Feature</directory>
</testsuite>
<testsuite name="Api.v1">
<directory suffix="Test.php">./tests/Api/v1</directory>
</testsuite>
</testsuites>
<php>
<server name="APP_ENV" value="testing"/>

View File

@ -0,0 +1,95 @@
<?php
namespace Tests\Feature\Auth;
use App\User;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\Config;
use Illuminate\Auth\Notifications\ResetPassword;
use Illuminate\Support\Facades\Notification;
use Tests\FeatureTestCase;
class ForgotPasswordControllerTest extends FeatureTestCase
{
/**
* @var \App\User
*/
protected $user;
/**
* @test
*/
public function test_submit_email_password_request_without_email_returns_validation_error()
{
$response = $this->json('POST', '/api/v1/user/password/lost', [
'email' => ''
]);
$response->assertStatus(422)
->assertJsonValidationErrors(['email']);
}
/**
* @test
*/
public function test_submit_email_password_request_with_invalid_email_returns_validation_error()
{
$response = $this->json('POST', '/api/v1/user/password/lost', [
'email' => 'nametest.com'
]);
$response->assertStatus(422)
->assertJsonValidationErrors(['email']);
}
/**
* @test
*/
public function test_submit_email_password_request_with_unknown_email_returns_validation_error()
{
$response = $this->json('POST', '/api/v1/user/password/lost', [
'email' => 'name@test.com'
]);
$response->assertStatus(422)
->assertJsonValidationErrors(['email']);
}
/**
* @test
*/
public function test_submit_email_password_request_returns_success()
{
Notification::fake();
$this->user = factory(User::class)->create();
$response = $this->json('POST', '/api/v1/user/password/lost', [
'email' => $this->user->email
]);
$response->assertStatus(200);
$token = \Illuminate\Support\Facades\DB::table('password_resets')->first();
$this->assertNotNull($token);
Notification::assertSentTo($this->user, ResetPassword::class, function ($notification, $channels) use ($token) {
return Hash::check($notification->token, $token->token) === true;
});
}
/**
* @test
*/
public function test_submit_email_password_request__in_demo_mode_returns_unauthorized()
{
Config::set('2fauth.config.isDemoApp', true);
$response = $this->json('POST', '/api/v1/user/password/lost', [
'email' => ''
]);
$response->assertStatus(401);
}
}

View File

@ -0,0 +1,81 @@
<?php
namespace Tests\Api\v1\Controllers\Auth;
use App\User;
use App\Group;
use Tests\FeatureTestCase;
use App\TwoFAccount;
class PasswordControllerTest extends FeatureTestCase
{
/**
* @var \App\User
*/
protected $user;
private const PASSWORD = 'password';
private const NEW_PASSWORD = 'newPassword';
/**
* @test
*/
public function setUp(): void
{
parent::setUp();
$this->user = factory(User::class)->create();
}
/**
* @test
*/
public function test_update_return_success()
{
$response = $this->actingAs($this->user, 'api')
->json('PATCH', '/api/v1/user/password', [
'currentPassword' => self::PASSWORD,
'password' => self::NEW_PASSWORD,
'password_confirmation' => self::NEW_PASSWORD,
])
->assertOk()
->assertJsonStructure([
'message',
]);
}
/**
* @test
*/
public function test_update_passing_bad_current_pwd_return_bad_request()
{
$response = $this->actingAs($this->user, 'api')
->json('PATCH', '/api/v1/user/password', [
'currentPassword' => self::NEW_PASSWORD,
'password' => self::NEW_PASSWORD,
'password_confirmation' => self::NEW_PASSWORD,
])
->assertStatus(400)
->assertJsonStructure([
'message',
]);
}
/**
* @test
*/
public function test_update_passing_invalid_data_return_validation_error()
{
$response = $this->actingAs($this->user, 'api')
->json('PATCH', '/api/v1/user/password', [
'currentPassword' => self::PASSWORD,
'password' => null,
'password_confirmation' => self::NEW_PASSWORD,
])
->assertStatus(422);
}
}

View File

@ -0,0 +1,59 @@
<?php
namespace Tests\Api\v1\Controllers\Auth;
use Tests\FeatureTestCase;
class RegisterControllerTest extends FeatureTestCase
{
private const USERNAME = 'john doe';
private const EMAIL = 'johndoe@example.org';
private const PASSWORD = 'password';
/**
* @test
*/
public function setUp(): void
{
parent::setUp();
}
/**
* @test
*/
public function test_register_returns_success()
{
$response = $this->json('POST', '/api/v1/user', [
'name' => self::USERNAME,
'email' => self::EMAIL,
'password' => self::PASSWORD,
'password_confirmation' => self::PASSWORD,
])
->assertCreated()
->assertJsonStructure([
'message',
'name',
])
->assertJsonFragment([
'name' => self::USERNAME,
]);
}
/**
* @test
*/
public function test_register_with_invalid_data_returns_validation_error()
{
$response = $this->json('POST', '/api/v1/user', [
'name' => null,
'email' => self::EMAIL,
'password' => self::PASSWORD,
'password_confirmation' => self::PASSWORD,
])
->assertStatus(422);
}
}

View File

@ -6,21 +6,22 @@
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\Password;
use Illuminate\Support\Facades\Notification;
use Tests\TestCase;
use Tests\FeatureTestCase;
class ResetPasswordTest extends TestCase
class ResetPasswordControllerTest extends FeatureTestCase
{
/** @var \App\User */
/**
* @var \App\User
*/
protected $user;
/**
* Testing submitting the reset password without
* email address.
* @test
*/
public function testSubmitResetPasswordWithoutInput()
public function test_submit_reset_password_without_input_returns_validation_error()
{
$response = $this->json('POST', '/api/password/reset', [
$response = $this->json('POST', '/api/v1/user/password/reset', [
'email' => '',
'password' => '',
'password_confirmation' => '',
@ -32,12 +33,11 @@ public function testSubmitResetPasswordWithoutInput()
}
/**
* Testing submitting the reset password with
* invalid input.
* @test
*/
public function testSubmitResetPasswordWithInvalidInput()
public function test_submit_reset_password_with_invalid_data_returns_validation_error()
{
$response = $this->json('POST', '/api/password/reset', [
$response = $this->json('POST', '/api/v1/user/password/reset', [
'email' => 'qsdqsdqsd',
'password' => 'foofoofoo',
'password_confirmation' => 'barbarbar',
@ -49,12 +49,11 @@ public function testSubmitResetPasswordWithInvalidInput()
}
/**
* Testing submitting the reset password with
* invalid input.
* @test
*/
public function testSubmitResetPasswordWithTooShortPasswords()
public function test_submit_reset_password_with_too_short_pwd_returns_validation_error()
{
$response = $this->json('POST', '/api/password/reset', [
$response = $this->json('POST', '/api/v1/user/password/reset', [
'email' => 'foo@bar.com',
'password' => 'foo',
'password_confirmation' => 'foo',
@ -66,23 +65,16 @@ public function testSubmitResetPasswordWithTooShortPasswords()
}
/**
* Testing submitting the rest password.
* @test
*/
public function testSubmitResetPassword()
public function test_submit_reset_password_returns_success()
{
Notification::fake();
$this->user = factory(User::class)->create([
'name' => 'user',
'email' => 'user@example.org',
'password' => bcrypt('password'),
'email_verified_at' => now(),
'remember_token' => \Illuminate\Support\Str::random(10)
]);
$this->user = factory(User::class)->create();
$token = Password::broker()->createToken($this->user);
$response = $this->json('POST', '/api/password/reset', [
$response = $this->json('POST', '/api/v1/user/password/reset', [
'email' => $this->user->email,
'password' => 'newpassword',
'password_confirmation' => 'newpassword',
@ -91,7 +83,7 @@ public function testSubmitResetPassword()
$this->user->refresh();
$response->assertStatus(200);
$response->assertOk();
$this->assertTrue(Hash::check('newpassword', $this->user->password));
}

View File

@ -0,0 +1,144 @@
<?php
namespace Tests\Api\v1\Controllers\Auth;
use App\User;
use Tests\FeatureTestCase;
class UserControllerTest extends FeatureTestCase
{
/**
* @var \App\User
*/
protected $user;
private const NEW_USERNAME = 'Jane DOE';
private const NEW_EMAIL = 'janedoe@example.org';
private const PASSWORD = 'password';
/**
* @test
*/
public function setUp(): void
{
parent::setUp();
$this->user = factory(User::class)->create();
}
/**
* @test
*/
public function test_show_existing_user_when_authenticated_returns_success()
{
$response = $this->actingAs($this->user, 'api')
->json('GET', '/api/v1/user')
->assertOk()
->assertExactJson([
'name' => $this->user->name,
'email' => $this->user->email,
]);
}
/**
* @test
*/
public function test_show_existing_user_when_anonymous_returns_success()
{
$response = $this->json('GET', '/api/v1/user/name')
->assertOk()
->assertExactJson([
'name' => $this->user->name,
]);
}
/**
* @test
*/
public function test_show_missing_user_returns_success_with_null_name()
{
User::destroy($this->user->id);
$response = $this->actingAs($this->user, 'api')
->json('GET', '/api/v1/user')
->assertOk()
->assertExactJson([
'name' => null,
]);
}
/**
* @test
*/
public function test_update_user_returns_success()
{
$response = $this->actingAs($this->user, 'api')
->json('PUT', '/api/v1/user', [
'name' => self::NEW_USERNAME,
'email' => self::NEW_EMAIL,
'password' => self::PASSWORD,
])
->assertOk()
->assertExactJson([
'name' => self::NEW_USERNAME,
'email' => self::NEW_EMAIL,
]);
}
/**
* @test
*/
public function test_update_user_in_demo_mode_returns_unchanged_user()
{
$settingService = resolve('App\Services\SettingServiceInterface');
$settingService->set('isDemoApp', true);
$response = $this->actingAs($this->user, 'api')
->json('PUT', '/api/v1/user', [
'name' => self::NEW_USERNAME,
'email' => self::NEW_EMAIL,
'password' => self::PASSWORD,
])
->assertOk()
->assertExactJson([
'name' => $this->user->name,
'email' => $this->user->email,
]);
}
/**
* @test
*/
public function test_update_user_passing_wrong_password_returns_bad_request()
{
$response = $this->actingAs($this->user, 'api')
->json('PUT', '/api/v1/user', [
'name' => self::NEW_USERNAME,
'email' => self::NEW_EMAIL,
'password' => 'wrongPassword',
])
->assertStatus(400);
}
/**
* @test
*/
public function test_update_user_with_invalid_data_returns_validation_error()
{
$response = $this->actingAs($this->user, 'api')
->json('PUT', '/api/v1/user', [
'name' => '',
'email' => '',
'password' => self::PASSWORD,
])
->assertStatus(422);
}
}

View File

@ -7,6 +7,10 @@
use Tests\FeatureTestCase;
use App\TwoFAccount;
/**
* @covers \App\Api\v1\Controllers\GroupController
*/
class GroupControllerTest extends FeatureTestCase
{
/**

View File

@ -6,6 +6,10 @@
use Illuminate\Foundation\Testing\WithoutMiddleware;
use Tests\TestCase;
/**
* @covers \App\Api\v1\Controllers\IconController
*/
class IconControllerTest extends TestCase
{

View File

@ -7,7 +7,11 @@
use App\TwoFAccount;
use Tests\Classes\LocalFile;
class QrcodeControllerTest extends FeatureTestCase
/**
* @covers \App\Api\v1\Controllers\QrCodeController
*/
class QrCodeControllerTest extends FeatureTestCase
{
/**

View File

@ -7,6 +7,10 @@
use Tests\FeatureTestCase;
use App\TwoFAccount;
/**
* @covers \App\Api\v1\Controllers\SettingController
*/
class SettingControllerTest extends FeatureTestCase
{
/**

View File

@ -3,11 +3,16 @@
namespace Tests\Api\v1\Unit;
use App\User;
use App\Group;
use Tests\FeatureTestCase;
use App\TwoFAccount;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Storage;
/**
* @covers \App\Api\v1\Controllers\TwoFAccountController
*/
class TwoFAccountControllerTest extends FeatureTestCase
{
/**
@ -15,6 +20,11 @@ class TwoFAccountControllerTest extends FeatureTestCase
*/
protected $user;
/**
* @var \App\Group
*/
protected $group;
private const ACCOUNT = 'account';
private const SERVICE = 'service';
private const SECRET = 'A4GRFHVVRBGY7UIW';
@ -156,6 +166,7 @@ public function setUp(): void
parent::setUp();
$this->user = factory(User::class)->create();
$this->group = factory(Group::class)->create();
}
@ -419,6 +430,87 @@ public function test_store_with_invalid_uri_returns_validation_error()
}
/**
* @test
*/
public function test_store_assigns_created_account_when_default_group_is_a_specific_one()
{
// Set the default group to a specific one
$settingService = resolve('App\Services\SettingServiceInterface');
$settingService->set('defaultGroup', $this->group->id);
$response = $this->actingAs($this->user, 'api')
->json('POST', '/api/v1/twofaccounts', [
'uri' => self::TOTP_SHORT_URI,
])
->assertJsonFragment([
'group_id' => $this->group->id
]);
}
/**
* @test
*/
public function test_store_assigns_created_account_when_default_group_is_the_active_one()
{
$settingService = resolve('App\Services\SettingServiceInterface');
// Set the default group to be the active one
$settingService->set('defaultGroup', -1);
// Set the active group
$settingService->set('activeGroup', 1);
$response = $this->actingAs($this->user, 'api')
->json('POST', '/api/v1/twofaccounts', [
'uri' => self::TOTP_SHORT_URI,
])
->assertJsonFragment([
'group_id' => 1
]);
}
/**
* @test
*/
public function test_store_assigns_created_account_when_default_group_is_no_group()
{
$settingService = resolve('App\Services\SettingServiceInterface');
// Set the default group to No group
$settingService->set('defaultGroup', 0);
$response = $this->actingAs($this->user, 'api')
->json('POST', '/api/v1/twofaccounts', [
'uri' => self::TOTP_SHORT_URI,
])
->assertJsonFragment([
'group_id' => null
]);
}
/**
* @test
*/
public function test_store_assigns_created_account_when_default_group_does_not_exist()
{
$settingService = resolve('App\Services\SettingServiceInterface');
// Set the default group to a non-existing one
$settingService->set('defaultGroup', 1000);
$response = $this->actingAs($this->user, 'api')
->json('POST', '/api/v1/twofaccounts', [
'uri' => self::TOTP_SHORT_URI,
])
->assertJsonFragment([
'group_id' => null
]);
}
/**
* @test
*/

View File

@ -1,261 +0,0 @@
<?php
namespace Tests\Feature;
use App\User;
use App\Group;
use Tests\TestCase;
use App\TwoFAccount;
class AccountsGroupTest extends TestCase
{
/** @var \App\User, \App\TwoFAccount, \App\Group */
protected $user, $twofaccounts, $group;
/**
* @test
*/
public function setUp(): void
{
parent::setUp();
$this->user = factory(User::class)->create();
$this->twofaccounts = factory(Twofaccount::class, 3)->create();
$this->group = factory(Group::class)->create();
}
/**
* test 2FAccounts creation associated to a user group via API
*
* @test
*/
public function testCreateAccountWhenDefaultGroupIsASpecificOne()
{
// Set the default group to the existing one
$response = $this->actingAs($this->user, 'api')
->json('POST', '/api/settings/options', [
'defaultGroup' => $this->group->id,
])
->assertStatus(200);
// Create the account
$response = $this->actingAs($this->user, 'api')
->json('POST', '/api/twofaccounts', [
'service' => 'testCreation',
'account' => 'test@example.org',
'uri' => 'otpauth://totp/test@test.com?secret=A4GRFHZVRBGY7UIW&issuer=test',
'icon' => 'test.png',
])
->assertStatus(201)
->assertJsonFragment([
'group_id' => $this->group->id
]);
}
/**
* test 2FAccounts creation associated to a user group via API
*
* @test
*/
public function testCreateAccountWhenDefaultGroupIsSetToActiveOne()
{
// Set the default group as the active one
$response = $this->actingAs($this->user, 'api')
->json('POST', '/api/settings/options', [
'defaultGroup' => -1,
])
->assertStatus(200);
// Set the active group
$response = $this->actingAs($this->user, 'api')
->json('POST', '/api/settings/options', [
'activeGroup' => 1,
])
->assertStatus(200);
// Create the account
$response = $this->actingAs($this->user, 'api')
->json('POST', '/api/twofaccounts', [
'service' => 'testCreation',
'account' => 'test@example.org',
'uri' => 'otpauth://totp/test@test.com?secret=A4GRFHZVRBGY7UIW&issuer=test',
'icon' => 'test.png',
])
->assertStatus(201)
->assertJsonFragment([
'group_id' => 1
]);
}
/**
* test 2FAccounts creation associated to a user group via API
*
* @test
*/
public function testCreateAccountWhenDefaultIsNoGroup()
{
// Set the default group to No group
$response = $this->actingAs($this->user, 'api')
->json('POST', '/api/settings/options', [
'defaultGroup' => 0,
])
->assertStatus(200);
// Create the account
$response = $this->actingAs($this->user, 'api')
->json('POST', '/api/twofaccounts', [
'service' => 'testCreation',
'account' => 'test@example.org',
'uri' => 'otpauth://totp/test@test.com?secret=A4GRFHZVRBGY7UIW&issuer=test',
'icon' => 'test.png',
])
->assertStatus(201)
->assertJsonMissing([
'group_id' => null
]);
}
/**
* test 2FAccounts creation associated to a user group via API
*
* @test
*/
public function testCreateAccountWhenDefaultGroupDoNotExists()
{
// Set the default group to a non existing one
$response = $this->actingAs($this->user, 'api')
->json('POST', '/api/settings/options', [
'defaultGroup' => 1000,
])
->assertStatus(200);
// Create the account
$response = $this->actingAs($this->user, 'api')
->json('POST', '/api/twofaccounts', [
'service' => 'testCreation',
'account' => 'test@example.org',
'uri' => 'otpauth://totp/test@test.com?secret=A4GRFHZVRBGY7UIW&issuer=test',
'icon' => 'test.png',
])
->assertStatus(201)
->assertJsonMissing([
'group_id' => null
]);
}
/**
* test 2FAccounts association with a user group via API
*
* @test
*/
public function testMoveAccountsToGroup()
{
// We associate all 3 accounts to the user group
$response = $this->actingAs($this->user, 'api')
->json('PATCH', '/api/group/accounts/', [
'groupId' => $this->group->id,
'accountsIds' => [1,2,3]
])
->assertJsonFragment([
'id' => $this->group->id,
'name' => $this->group->name
])
->assertStatus(200);
// test if the accounts have the correct foreign key
$response = $this->actingAs($this->user, 'api')
->json('GET', '/api/twofaccounts/1')
->assertJsonFragment([
'group_id' => $this->group->id
]);
$response = $this->actingAs($this->user, 'api')
->json('GET', '/api/twofaccounts/2')
->assertJsonFragment([
'group_id' => $this->group->id
]);
$response = $this->actingAs($this->user, 'api')
->json('GET', '/api/twofaccounts/3')
->assertJsonFragment([
'group_id' => $this->group->id
]);
// test the accounts count of the user group
$response = $this->actingAs($this->user, 'api')
->json('GET', '/api/groups')
->assertJsonFragment([
'twofaccounts_count' => 3
]
);
}
/**
* test 2FAccounts association with a missing group via API
*
* @test
*/
public function testMoveAccountsToMissingGroup()
{
$response = $this->actingAs($this->user, 'api')
->json('PATCH', '/api/group/accounts/', [
'groupId' => '1000',
'accountsIds' => $this->twofaccounts->keys()
])
->assertStatus(404);
}
/**
* test 2FAccounts association with the pseudo group via API
*
* @test
*/
public function testMoveAccountsToPseudoGroup()
{
$response = $this->actingAs($this->user, 'api')
->json('PATCH', '/api/group/accounts/', [
'groupId' => $this->group->id,
'accountsIds' => [1,2,3]
]);
// We associate the first account to the pseudo group
$response = $this->actingAs($this->user, 'api')
->json('PATCH', '/api/group/accounts/', [
'groupId' => 0,
'accountsIds' => [1]
])
->assertStatus(200);
// test if the forein keys are set to NULL
$response = $this->actingAs($this->user, 'api')
->json('GET', '/api/twofaccounts/1')
->assertJsonFragment([
'group_id' => null
]);
// test the accounts count of the group
$response = $this->actingAs($this->user, 'api')
->json('GET', '/api/groups')
->assertJsonFragment([
'twofaccounts_count' => 3, // the 3 accounts for 'all'
'twofaccounts_count' => 2 // the 2 accounts that remain in the user group
]
);
}
}

View File

@ -1,103 +0,0 @@
<?php
namespace Tests\Feature\Auth;
use App\User;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\Config;
use Illuminate\Support\Facades\Password;
use Illuminate\Auth\Notifications\ResetPassword;
use Illuminate\Support\Facades\Notification;
use Tests\TestCase;
class ForgotPasswordTest extends TestCase
{
/** @var \App\User */
protected $user;
/**
* Testing submitting the email password request without
* email address.
*/
public function testSubmitEmailPasswordRequestWithoutEmail()
{
$response = $this->json('POST', '/api/password/email', [
'email' => ''
]);
$response->assertStatus(422)
->assertJsonValidationErrors(['email']);
}
/**
* Testing submitting the email password request with an invalid
* email address.
*/
public function testSubmitEmailPasswordRequestWithInvalidEmail()
{
$response = $this->json('POST', '/api/password/email', [
'email' => 'nametest.com'
]);
$response->assertStatus(422)
->assertJsonValidationErrors(['email']);
}
/**
* Testing submitting the email password request with an unknown
* email address.
*/
public function testSubmitEmailPasswordRequestWithUnknownEmail()
{
$response = $this->json('POST', '/api/password/email', [
'email' => 'name@test.com'
]);
$response->assertStatus(422)
->assertJsonValidationErrors(['email']);
}
/**
* Testing submitting the email password request with a valid email address.
*/
public function testSubmitEmailPasswordRequest()
{
Notification::fake();
$this->user = factory(User::class)->create([
'name' => 'user',
'email' => 'user@example.org',
'password' => bcrypt('password'),
'email_verified_at' => now(),
'remember_token' => \Illuminate\Support\Str::random(10),
]);
$response = $this->json('POST', '/api/password/email', [
'email' => $this->user->email
]);
$response->assertStatus(200);
$token = \Illuminate\Support\Facades\DB::table('password_resets')->first();
$this->assertNotNull($token);
Notification::assertSentTo($this->user, ResetPassword::class, function ($notification, $channels) use ($token) {
return Hash::check($notification->token, $token->token) === true;
});
}
/**
* Testing submitting the email password request in Demo mode
*/
public function testSubmitEmailPasswordRequestInDemoMode()
{
Config::set('2fauth.config.isDemoApp', true);
$response = $this->json('POST', '/api/password/email', [
'email' => ''
]);
$response->assertStatus(401);
}
}

View File

@ -3,18 +3,22 @@
namespace Tests\Feature\Auth;
use App\User;
use Tests\TestCase;
use Tests\FeatureTestCase;
use Illuminate\Auth\Authenticatable;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Hash;
use Illuminate\Auth\RequestGuard;
use Illuminate\Support\Facades\Config;
class LoginTest extends TestCase
class LoginTest extends FeatureTestCase
{
/** @var \App\User */
/**
* @var \App\User
*/
protected $user;
private const PASSWORD = 'password';
private const WRONG_PASSWORD = 'wrong_password';
/**
* @test
@ -28,45 +32,38 @@ public function setUp(): void
/**
* test User login via API
*
* @test
*/
public function testUserLogin()
public function test_user_login_returns_success()
{
$response = $this->json('POST', '/api/login', [
$response = $this->json('POST', '/user/login', [
'email' => $this->user->email,
'password' => 'password'
'password' => self::PASSWORD
])
->assertOk()
->assertExactJson([
'message' => 'authenticated',
'name' => $this->user->name,
]);
$response->assertStatus(200)
->assertJsonStructure([
'message' => ['token']
]);
}
/**
* test User login via API
*
* @test
*/
public function testUserLoginAlreadyAuthenticated()
public function test_user_login_already_authenticated_returns_bad_request()
{
$response = $this->json('POST', '/api/login', [
$response = $this->json('POST', '/user/login', [
'email' => $this->user->email,
'password' => 'password'
'password' => self::PASSWORD
]);
$response = $this->actingAs($this->user, 'api')
->json('POST', '/api/login', [
->json('POST', '/user/login', [
'email' => $this->user->email,
'password' => 'password'
]);
$response->assertStatus(400)
'password' => self::PASSWORD
])
->assertStatus(400)
->assertJson([
'message' => __('auth.already_authenticated')
]);
@ -74,79 +71,71 @@ public function testUserLoginAlreadyAuthenticated()
/**
* test User login with missing values via API
*
* @test
*/
public function testUserLoginWithMissingValues()
public function test_user_login_with_missing_data_returns_validation_error()
{
$response = $this->json('POST', '/api/login', [
$response = $this->json('POST', '/user/login', [
'email' => '',
'password' => ''
])
->assertStatus(422)
->assertJsonValidationErrors([
'email',
'password'
]);
$response->assertStatus(422)
->assertJsonValidationErrors([
'email',
'password'
]);
}
/**
* test User login with invalid credentials via API
*
* @test
*/
public function testUserLoginWithInvalidCredential()
public function test_user_login_with_invalid_credentials_returns_validation_error()
{
$response = $this->json('POST', '/api/login', [
$response = $this->json('POST', '/user/login', [
'email' => $this->user->email,
'password' => 'badPassword'
'password' => self::WRONG_PASSWORD
])
->assertStatus(401)
->assertJson([
'message' => 'unauthorised'
]);
$response->assertStatus(401)
->assertJson([
'message' => 'unauthorised'
]);
}
/**
* test User login with invalid credentials via API
*
* @test
*/
public function testTooManyAttempsWithInvalidCredential()
public function test_too_many_login_attempts_with_invalid_credentials_returns_too_many_request_error()
{
$response = $this->json('POST', '/api/login', [
$response = $this->json('POST', '/user/login', [
'email' => $this->user->email,
'password' => 'badPassword'
'password' => self::WRONG_PASSWORD
]);
$response = $this->json('POST', '/api/login', [
$response = $this->json('POST', '/user/login', [
'email' => $this->user->email,
'password' => 'badPassword'
'password' => self::WRONG_PASSWORD
]);
$response = $this->json('POST', '/api/login', [
$response = $this->json('POST', '/user/login', [
'email' => $this->user->email,
'password' => 'badPassword'
'password' => self::WRONG_PASSWORD
]);
$response = $this->json('POST', '/api/login', [
$response = $this->json('POST', '/user/login', [
'email' => $this->user->email,
'password' => 'badPassword'
'password' => self::WRONG_PASSWORD
]);
$response = $this->json('POST', '/api/login', [
$response = $this->json('POST', '/user/login', [
'email' => $this->user->email,
'password' => 'badPassword'
'password' => self::WRONG_PASSWORD
]);
$response = $this->json('POST', '/api/login', [
$response = $this->json('POST', '/user/login', [
'email' => $this->user->email,
'password' => 'badPassword'
'password' => self::WRONG_PASSWORD
]);
$response->assertStatus(429);
@ -154,46 +143,47 @@ public function testTooManyAttempsWithInvalidCredential()
/**
* test User logout via API
*
* @test
*/
public function testUserLogout()
public function test_user_logout_returns_validation_success()
{
$response = $this->json('POST', '/api/login', [
$response = $this->json('POST', '/user/login', [
'email' => $this->user->email,
'password' => 'password'
'password' => self::PASSWORD
]);
$headers = ['Authorization' => "Bearer " . $response->original['message']['token']];
$response = $this->json('POST', '/api/logout', [], $headers)
->assertStatus(200)
->assertJson([
$response = $this->actingAs($this->user, 'api')
->json('GET', '/user/logout')
->assertOk()
->assertExactJson([
'message' => 'signed out',
]);
}
/**
* test User logout after inactivity via API
*
* @test
*/
public function testUserLogoutAfterInactivity()
public function test_user_logout_after_inactivity_returns_unauthorized()
{
// Set the autolock period to 1 minute
$settingService = resolve('App\Services\SettingServiceInterface');
$settingService->set('kickUserAfter', 1);
$response = $this->json('POST', '/user/login', [
'email' => $this->user->email,
'password' => self::PASSWORD
]);
// Ping a protected endpoint to log last_seen_at time
$response = $this->actingAs($this->user, 'api')
->json('POST', '/api/settings/options', [
'kickUserAfter' => '1'])
->assertStatus(200);
->json('GET', '/api/v1/twofaccounts');
sleep(61);
// Ping a restricted endpoint to log last_seen_at time
$response = $this->actingAs($this->user, 'api')
->json('GET', '/api/settings/account')
->assertStatus(401);
->json('GET', '/api/v1/twofaccounts')
->assertUnauthorized();
}
}

View File

@ -1,124 +0,0 @@
<?php
namespace Tests\Feature\Auth;
use App\User;
use Tests\TestCase;
class RegisterTest extends TestCase
{
/** @var \App\User */
protected $user;
/**
* @test
*/
public function setUp(): void
{
parent::setUp();
$this->user = factory(User::class)->create();
}
/**
* test Existing user count via API
*
* @test
*/
public function testExistingUserCount()
{
$response = $this->json('POST', '/api/checkuser')
->assertStatus(200)
->assertJson([
'username' => $this->user->name,
]);
}
/**
* test creation of another user via API
*
* @test
*/
public function testUserCreationWithAnExistingUser()
{
$response = $this->json('POST', '/api/register', [
'name' => 'testCreate',
'email' => 'testCreate@example.org',
'password' => 'test',
'password_confirmation' => 'test',
]);
$response->assertStatus(422);
}
/**
* test User creation with missing values via API
*
* @test
*/
public function testUserCreationWithMissingValues()
{
// we delete the existing user
User::destroy(1);
$response = $this->json('POST', '/api/register', [
'name' => '',
'email' => '',
'password' => '',
'password_confirmation' => '',
]);
$response->assertStatus(422);
}
/**
* test User creation with invalid values via API
*
* @test
*/
public function testUserCreationWithInvalidData()
{
// we delete the existing user
User::destroy(1);
$response = $this->json('POST', '/api/register', [
'name' => 'testInvalid',
'email' => 'email',
'password' => 'test',
'password_confirmation' => 'tset',
]);
$response->assertStatus(422);
}
/**
* test User creation via API
*
* @test
*/
public function testUserCreation()
{
// we delete the existing user
User::destroy(1);
$response = $this->json('POST', '/api/register', [
'name' => 'newUser',
'email' => 'newUser@example.org',
'password' => 'password',
'password_confirmation' => 'password',
]);
$response->assertStatus(200)
->assertJsonStructure([
'message' => ['token', 'name']
]);
}
}

View File

@ -1,288 +0,0 @@
<?php
namespace Tests\Feature;
use App\User;
use Tests\TestCase;
use App\TwoFAccount;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Crypt;
class ProtectDbTest extends TestCase
{
/** @var \App\User, \App\TwoFAccount */
protected $user, $twofaccounts;
/**
* @test
*/
public function setUp(): void
{
parent::setUp();
$this->user = factory(User::class)->create();
$this->twofaccount = factory(Twofaccount::class,)->create([
'service' => 'test',
'account' => 'test@test.com',
'uri' => 'otpauth://totp/test@test.com?secret=A4GRFHVVRBGY7UIW&issuer=test',
]);
$this->twofaccountAlt = factory(Twofaccount::class,)->create([
'service' => 'testAlt',
'account' => 'testAlt@test.com',
'uri' => 'otpauth://totp/testAlt@test.com?secret=A4GRFHVVRBGY7UIW&issuer=testAlt',
]);
}
/**
* test db encryption via API
*
* @test
*/
public function testDbEncryption()
{
// Encrypt db
$response = $this->actingAs($this->user, 'api')
->json('POST', '/api/settings/options', [
'useEncryption' => true,
])
->assertStatus(200);
// Get the raw encrypted records
$encrypted = DB::table('twofaccounts')->find($this->twofaccount->id);
$encryptedAlt = DB::table('twofaccounts')->find($this->twofaccountAlt->id);
// Get the accounts via API and check their consistency with raw data
$response = $this->actingAs($this->user, 'api')
->json('GET', '/api/twofaccounts/' . $this->twofaccount->id)
->assertStatus(200)
->assertJsonFragment([
'service' => 'test',
'account' => Crypt::decryptString($encrypted->account),
]);
$response = $this->actingAs($this->user, 'api')
->json('GET', '/api/twofaccounts/' . $this->twofaccountAlt->id)
->assertStatus(200)
->assertJsonFragment([
'service' => 'testAlt',
'account' => Crypt::decryptString($encryptedAlt->account),
]);
}
/**
* test Account update on protected DB via API
*
* @test
*/
public function testTwofaccountUpdateOnProtectedDb()
{
// Encrypt db
$response = $this->actingAs($this->user, 'api')
->json('POST', '/api/settings/options', [
'useEncryption' => true,
])
->assertStatus(200);
// Only the Account field is encrypted
$response = $this->actingAs($this->user, 'api')
->json('PUT', '/api/twofaccounts/' . $this->twofaccount->id, [
'service' => 'testUpdate',
'account' => 'testUpdate@test.com',
'otpType' => 'totp',
'secret' => 'A4GRFHVVRBGY7UIW',
'secretIsBase32Encoded' => 1,
'digits' => 8,
'totpPeriod' => 30,
'algorithm' => 'sha256',
])
->assertStatus(200)
->assertJsonFragment([
'id' => 1,
'service' => 'testUpdate',
'account' => 'testUpdate@test.com',
]);
}
/**
* test db encryption via API
*
* @test
*/
public function testPreventDbEncryptionOnDbAlreadyEncrypted()
{
// Encrypt db
$response = $this->actingAs($this->user, 'api')
->json('POST', '/api/settings/options', [
'useEncryption' => true,
]);
// Set the option again to force another encryption pass
$response = $this->actingAs($this->user, 'api')
->json('POST', '/api/settings/options', [
'useEncryption' => true,
]);
// Get the account, it should be readable
$response = $this->actingAs($this->user, 'api')
->json('GET', '/api/twofaccounts/' . $this->twofaccount->id)
->assertStatus(200)
->assertJsonFragment([
'service' => 'test',
'account' => 'test@test.com',
]);
$response = $this->actingAs($this->user, 'api')
->json('GET', '/api/twofaccounts/' . $this->twofaccountAlt->id)
->assertStatus(200)
->assertJsonFragment([
'service' => 'testAlt',
'account' => 'testAlt@test.com',
]);
}
/**
* test db deciphering via API
*
* @test
*/
public function testDbDeciphering()
{
// Encrypt db
$response = $this->actingAs($this->user, 'api')
->json('POST', '/api/settings/options', [
'useEncryption' => true,
]);
// Decipher db
$response = $this->actingAs($this->user, 'api')
->json('POST', '/api/settings/options', [
'useEncryption' => false,
])
->assertStatus(200);
// Get the accounts, they should be readable
$response = $this->actingAs($this->user, 'api')
->json('GET', '/api/twofaccounts/' . $this->twofaccount->id)
->assertStatus(200)
->assertJsonFragment([
'service' => 'test',
'account' => 'test@test.com',
]);
$response = $this->actingAs($this->user, 'api')
->json('GET', '/api/twofaccounts/' . $this->twofaccountAlt->id)
->assertStatus(200)
->assertJsonFragment([
'service' => 'testAlt',
'account' => 'testAlt@test.com',
]);
}
/**
* test Protect DB option not being persisted if encryption fails via API
*
* @test
*/
public function testAbortEncryptionIfSomethingGoesWrong()
{
// Set no APP_KEY to break Laravel encryption capability
config(['app.key' => '']);
// Decipher db
$response = $this->actingAs($this->user, 'api')
->json('POST', '/api/settings/options', [
'useEncryption' => true,
])
->assertStatus(400);
// Check ProtectDB option is not active
$response = $this->actingAs($this->user, 'api')
->json('GET', '/api/settings/options')
->assertJsonFragment([
'useEncryption' => false
]);
}
/**
* test Protect DB option not being persisted if decyphering fails via API
*
* @test
*/
public function testAbortDecipheringIfSomethingGoesWrong()
{
// Encrypt db
$response = $this->actingAs($this->user, 'api')
->json('POST', '/api/settings/options', [
'useEncryption' => true,
])
->assertStatus(200);
// alter the ciphertext to make deciphering impossible
$affected = DB::table('twofaccounts')
->where('id', 1)
->update(['account' => 'xxxxxxxxx', 'uri' => 'yyyyyyyyy']);
// Decipher db
$response = $this->actingAs($this->user, 'api')
->json('POST', '/api/settings/options', [
'useEncryption' => false,
])
->assertStatus(400);
// Check ProtectDB option has been restored
$response = $this->actingAs($this->user, 'api')
->json('GET', '/api/settings/options')
->assertJsonFragment([
'useEncryption' => true
]);
}
/**
* test bad payload don't breaks anything via API
*
* @test
*/
public function testBadPayloadDontBreakEncryptedAccountFetching()
{
// Encrypt db
$response = $this->actingAs($this->user, 'api')
->json('POST', '/api/settings/options', [
'useEncryption' => true,
])
->assertStatus(200);
// break the payload
DB::table('twofaccounts')
->where('id', 1)
->update([
'account' => 'YouShallNotPass',
'uri' => 'PasDeBrasPasDeChocolat',
]);
$response = $this->actingAs($this->user, 'api')
->json('GET', '/api/twofaccounts/1')
->assertStatus(200)
->assertJsonFragment([
'account' => '*encrypted*',
'service' => 'test',
'group_id' => null,
'isConsistent' => false,
'otpType' => null,
'digits' => null,
'hotpCounter' => null,
'totpPeriod' => null,
])
->assertJsonMissing([
'uri' => '*encrypted*',
]);
}
}

View File

@ -1,10 +1,10 @@
<?php
namespace Tests\Unit;
namespace Tests\Feature;
use Tests\TestCase;
use Tests\FeatureTestCase;
class RouteTest extends TestCase
class RouteTest extends FeatureTestCase
{
/**
@ -12,7 +12,7 @@ class RouteTest extends TestCase
*
* @test
*/
public function testLandingViewIsReturned()
public function test_landing_view_is_returned()
{
$response = $this->get(route('landing', ['any' => '/']));
@ -25,7 +25,7 @@ public function testLandingViewIsReturned()
*
* @test
*/
public function testExceptionHandlerWithWebRoute()
public function test_exception_handler_with_web_route()
{
$response = $this->post('/');

View File

@ -0,0 +1,197 @@
<?php
namespace Tests\Feature;
use Tests\FeatureTestCase;
use Illuminate\Support\Facades\DB;
class AppstractOptionsServiceTest extends FeatureTestCase
{
/**
* App\Services\SettingServiceInterface $settingService
*/
protected $settingService;
private const KEY = 'key';
private const VALUE = 'value';
private const SETTING_NAME = 'MySetting';
private const SETTING_NAME_ALT = 'MySettingAlt';
private const SETTING_VALUE_STRING = 'MyValue';
private const SETTING_VALUE_TRUE_TRANSFORMED = '{{1}}';
private const SETTING_VALUE_FALSE_TRANSFORMED = '{{}}';
private const SETTING_VALUE_INT = 10;
/**
* @test
*/
public function setUp() : void
{
parent::setUp();
$this->settingService = $this->app->make('App\Services\SettingServiceInterface');
}
/**
* @test
*/
public function test_get_string_setting_returns_correct_value()
{
DB::table('options')->insert(
[self::KEY => self::SETTING_NAME, self::VALUE => strval(self::SETTING_VALUE_STRING)]
);
$this->assertEquals(self::SETTING_VALUE_STRING, $this->settingService->get(self::SETTING_NAME));
}
/**
* @test
*/
public function test_get_boolean_setting_returns_true()
{
DB::table('options')->insert(
[self::KEY => self::SETTING_NAME, self::VALUE => strval(self::SETTING_VALUE_TRUE_TRANSFORMED)]
);
$this->assertEquals(true, $this->settingService->get(self::SETTING_NAME));
}
/**
* @test
*/
public function test_get_boolean_setting_returns_false()
{
DB::table('options')->insert(
[self::KEY => self::SETTING_NAME, self::VALUE => strval(self::SETTING_VALUE_FALSE_TRANSFORMED)]
);
$this->assertEquals(false, $this->settingService->get(self::SETTING_NAME));
}
/**
* @test
*/
public function test_get_int_setting_returns_int()
{
DB::table('options')->insert(
[self::KEY => self::SETTING_NAME, self::VALUE => strval(self::SETTING_VALUE_INT)]
);
$value = $this->settingService->get(self::SETTING_NAME);
$this->assertEquals(self::SETTING_VALUE_INT, $value);
$this->assertIsInt($value);
}
/**
* @test
*/
public function test_all_returns_native_and_user_settings()
{
$native_options = config('2fauth.options');
DB::table('options')->insert(
[self::KEY => self::SETTING_NAME, self::VALUE => strval(self::SETTING_VALUE_STRING)]
);
$all = $this->settingService->all();
$this->assertCount(count($native_options)+1, $all);
$this->assertArrayHasKey(self::SETTING_NAME, $all);
$this->assertEquals($all[self::SETTING_NAME], self::SETTING_VALUE_STRING);
foreach ($native_options as $key => $val) {
$this->assertArrayHasKey($key, $all);
$this->assertEquals($all[$key], $val);
}
}
/**
* @test
*/
public function test_set_setting_persist_correct_value()
{
$value = $this->settingService->set(self::SETTING_NAME, self::SETTING_VALUE_STRING);
$this->assertDatabaseHas('options', [
self::KEY => self::SETTING_NAME,
self::VALUE => self::SETTING_VALUE_STRING
]);
}
/**
* @test
*/
public function test_set_array_of_settings_persist_correct_values()
{
$value = $this->settingService->set([
self::SETTING_NAME => self::SETTING_VALUE_STRING,
self::SETTING_NAME_ALT => self::SETTING_VALUE_INT,
]);
$this->assertDatabaseHas('options', [
self::KEY => self::SETTING_NAME,
self::VALUE => self::SETTING_VALUE_STRING
]);
$this->assertDatabaseHas('options', [
self::KEY => self::SETTING_NAME_ALT,
self::VALUE => self::SETTING_VALUE_INT
]);
}
/**
* @test
*/
public function test_set_true_setting_persist_transformed_boolean()
{
$value = $this->settingService->set(self::SETTING_NAME, true);
$this->assertDatabaseHas('options', [
self::KEY => self::SETTING_NAME,
self::VALUE => self::SETTING_VALUE_TRUE_TRANSFORMED
]);
}
/**
* @test
*/
public function test_set_false_setting_persist_transformed_boolean()
{
$value = $this->settingService->set(self::SETTING_NAME, false);
$this->assertDatabaseHas('options', [
self::KEY => self::SETTING_NAME,
self::VALUE => self::SETTING_VALUE_FALSE_TRANSFORMED
]);
}
/**
* @test
*/
public function test_del_remove_setting_from_db()
{
DB::table('options')->insert(
[self::KEY => self::SETTING_NAME, self::VALUE => strval(self::SETTING_VALUE_STRING)]
);
$value = $this->settingService->delete(self::SETTING_NAME);
$this->assertDatabaseMissing('options', [
self::KEY => self::SETTING_NAME,
self::VALUE => self::SETTING_VALUE_STRING
]);
}
}

View File

@ -0,0 +1,621 @@
<?php
namespace Tests\Feature;
use App\TwoFAccount;
use Tests\FeatureTestCase;
use Illuminate\Support\Facades\DB;
/**
* @covers \App\Services\TwoFAccountService
*/
class TwoFAccountServiceTest extends FeatureTestCase
{
/**
* App\Services\SettingServiceInterface $settingService
*/
protected $twofaccountService;
/**
* App\TwoFAccount $customTotpTwofaccount
*/
protected $customTotpTwofaccount;
/**
* App\TwoFAccount $customTotpTwofaccount
*/
protected $customHotpTwofaccount;
private const ACCOUNT = 'account';
private const SERVICE = 'service';
private const SECRET = 'A4GRFHVVRBGY7UIW';
private const ALGORITHM_DEFAULT = 'sha1';
private const ALGORITHM_CUSTOM = 'sha256';
private const DIGITS_DEFAULT = 6;
private const DIGITS_CUSTOM = 7;
private const PERIOD_DEFAULT = 30;
private const PERIOD_CUSTOM = 40;
private const COUNTER_DEFAULT = 0;
private const COUNTER_CUSTOM = 5;
private const IMAGE = 'https%3A%2F%2Fen.opensuse.org%2Fimages%2F4%2F44%2FButton-filled-colour.png';
private const ICON = 'test.png';
private const TOTP_FULL_CUSTOM_URI = 'otpauth://totp/'.self::SERVICE.':'.self::ACCOUNT.'?secret='.self::SECRET.'&issuer='.self::SERVICE.'&digits='.self::DIGITS_CUSTOM.'&period='.self::PERIOD_CUSTOM.'&algorithm='.self::ALGORITHM_CUSTOM.'&image='.self::IMAGE;
private const HOTP_FULL_CUSTOM_URI = 'otpauth://hotp/'.self::SERVICE.':'.self::ACCOUNT.'?secret='.self::SECRET.'&issuer='.self::SERVICE.'&digits='.self::DIGITS_CUSTOM.'&counter='.self::COUNTER_CUSTOM.'&algorithm='.self::ALGORITHM_CUSTOM.'&image='.self::IMAGE;
private const TOTP_SHORT_URI = 'otpauth://totp/'.self::ACCOUNT.'?secret='.self::SECRET;
private const HOTP_SHORT_URI = 'otpauth://hotp/'.self::ACCOUNT.'?secret='.self::SECRET;
private const TOTP_URI_WITH_UNREACHABLE_IMAGE = 'otpauth://totp/service:account?secret=A4GRFHVVRBGY7UIW&image=https%3A%2F%2Fen.opensuse.org%2Fimage.png';
private const INVALID_OTPAUTH_URI = 'otpauth://Xotp/'.self::ACCOUNT.'?secret='.self::SECRET;
private const ARRAY_OF_FULL_VALID_PARAMETERS_FOR_CUSTOM_TOTP = [
'service' => self::SERVICE,
'account' => self::ACCOUNT,
'icon' => self::ICON,
'otp_type' => 'totp',
'secret' => self::SECRET,
'digits' => self::DIGITS_CUSTOM,
'algorithm' => self::ALGORITHM_CUSTOM,
'period' => self::PERIOD_CUSTOM,
'counter' => null,
];
private const ARRAY_OF_MINIMUM_VALID_PARAMETERS_FOR_TOTP = [
'account' => self::ACCOUNT,
'otp_type' => 'totp',
'secret' => self::SECRET,
];
private const ARRAY_OF_PARAMETERS_FOR_UNSUPPORTED_OTP_TYPE = [
'account' => self::ACCOUNT,
'otp_type' => 'Xotp',
'secret' => self::SECRET,
];
private const ARRAY_OF_INVALID_PARAMETERS_FOR_TOTP = [
'account' => self::ACCOUNT,
'otp_type' => 'totp',
'secret' => 0,
];
private const ARRAY_OF_FULL_VALID_PARAMETERS_FOR_CUSTOM_HOTP = [
'service' => self::SERVICE,
'account' => self::ACCOUNT,
'icon' => self::ICON,
'otp_type' => 'hotp',
'secret' => self::SECRET,
'digits' => self::DIGITS_CUSTOM,
'algorithm' => self::ALGORITHM_CUSTOM,
'period' => null,
'counter' => self::COUNTER_CUSTOM,
];
private const ARRAY_OF_MINIMUM_VALID_PARAMETERS_FOR_HOTP = [
'account' => self::ACCOUNT,
'otp_type' => 'hotp',
'secret' => self::SECRET,
];
/**
* @test
*/
public function setUp() : void
{
parent::setUp();
$this->twofaccountService = $this->app->make('App\Services\TwoFAccountService');
$this->customTotpTwofaccount = new TwoFAccount;
$this->customTotpTwofaccount->legacy_uri = self::TOTP_FULL_CUSTOM_URI;
$this->customTotpTwofaccount->service = self::SERVICE;
$this->customTotpTwofaccount->account = self::ACCOUNT;
$this->customTotpTwofaccount->icon = self::ICON;
$this->customTotpTwofaccount->otp_type = 'totp';
$this->customTotpTwofaccount->secret = self::SECRET;
$this->customTotpTwofaccount->digits = self::DIGITS_CUSTOM;
$this->customTotpTwofaccount->algorithm = self::ALGORITHM_CUSTOM;
$this->customTotpTwofaccount->period = self::PERIOD_CUSTOM;
$this->customTotpTwofaccount->counter = null;
$this->customTotpTwofaccount->save();
$this->customHotpTwofaccount = new TwoFAccount;
$this->customHotpTwofaccount->legacy_uri = self::HOTP_FULL_CUSTOM_URI;
$this->customHotpTwofaccount->service = self::SERVICE;
$this->customHotpTwofaccount->account = self::ACCOUNT;
$this->customHotpTwofaccount->icon = self::ICON;
$this->customHotpTwofaccount->otp_type = 'hotp';
$this->customHotpTwofaccount->secret = self::SECRET;
$this->customHotpTwofaccount->digits = self::DIGITS_CUSTOM;
$this->customHotpTwofaccount->algorithm = self::ALGORITHM_CUSTOM;
$this->customHotpTwofaccount->period = null;
$this->customHotpTwofaccount->counter = self::COUNTER_CUSTOM;
$this->customHotpTwofaccount->save();
}
/**
* @test
*/
public function test_create_custom_totp_from_uri_returns_correct_value()
{
$twofaccount = $this->twofaccountService->createFromUri(self::TOTP_FULL_CUSTOM_URI);
$this->assertEquals('totp', $twofaccount->otp_type);
$this->assertEquals(self::TOTP_FULL_CUSTOM_URI, $twofaccount->legacy_uri);
$this->assertEquals(self::SERVICE, $twofaccount->service);
$this->assertEquals(self::ACCOUNT, $twofaccount->account);
$this->assertEquals(self::SECRET, $twofaccount->secret);
$this->assertEquals(self::DIGITS_CUSTOM, $twofaccount->digits);
$this->assertEquals(self::PERIOD_CUSTOM, $twofaccount->period);
$this->assertEquals(null, $twofaccount->counter);
$this->assertEquals(self::ALGORITHM_CUSTOM, $twofaccount->algorithm);
$this->assertStringEndsWith('.png',$twofaccount->icon);
}
/**
* @test
*/
public function test_create_basic_totp_from_uri_returns_default_value()
{
$twofaccount = $this->twofaccountService->createFromUri(self::TOTP_SHORT_URI);
$this->assertEquals('totp', $twofaccount->otp_type);
$this->assertEquals(self::TOTP_SHORT_URI, $twofaccount->legacy_uri);
$this->assertEquals(self::ACCOUNT, $twofaccount->account);
$this->assertEquals(null, $twofaccount->service);
$this->assertEquals(self::SECRET, $twofaccount->secret);
$this->assertEquals(self::DIGITS_DEFAULT, $twofaccount->digits);
$this->assertEquals(self::PERIOD_DEFAULT, $twofaccount->period);
$this->assertEquals(null, $twofaccount->counter);
$this->assertEquals(self::ALGORITHM_DEFAULT, $twofaccount->algorithm);
$this->assertEquals(null, $twofaccount->icon);
}
/**
* @test
*/
public function test_create_custom_hotp_from_uri_returns_correct_value()
{
$twofaccount = $this->twofaccountService->createFromUri(self::HOTP_FULL_CUSTOM_URI);
$this->assertEquals('hotp', $twofaccount->otp_type);
$this->assertEquals(self::HOTP_FULL_CUSTOM_URI, $twofaccount->legacy_uri);
$this->assertEquals(self::SERVICE, $twofaccount->service);
$this->assertEquals(self::ACCOUNT, $twofaccount->account);
$this->assertEquals(self::SECRET, $twofaccount->secret);
$this->assertEquals(self::DIGITS_CUSTOM, $twofaccount->digits);
$this->assertEquals(null, $twofaccount->period);
$this->assertEquals(self::COUNTER_CUSTOM, $twofaccount->counter);
$this->assertEquals(self::ALGORITHM_CUSTOM, $twofaccount->algorithm);
$this->assertStringEndsWith('.png',$twofaccount->icon);
}
/**
* @test
*/
public function test_create_basic_hotp_from_uri_returns_default_value()
{
$twofaccount = $this->twofaccountService->createFromUri(self::HOTP_SHORT_URI);
$this->assertEquals('hotp', $twofaccount->otp_type);
$this->assertEquals(self::HOTP_SHORT_URI, $twofaccount->legacy_uri);
$this->assertEquals(null, $twofaccount->service);
$this->assertEquals(self::ACCOUNT, $twofaccount->account);
$this->assertEquals(self::SECRET, $twofaccount->secret);
$this->assertEquals(self::DIGITS_DEFAULT, $twofaccount->digits);
$this->assertEquals(null, $twofaccount->period);
$this->assertEquals(self::COUNTER_DEFAULT, $twofaccount->counter);
$this->assertEquals(self::ALGORITHM_DEFAULT, $twofaccount->algorithm);
$this->assertEquals(null, $twofaccount->icon);
}
/**
* @test
*/
public function test_create_from_uri_persists_to_db()
{
$twofaccount = $this->twofaccountService->createFromUri(self::TOTP_SHORT_URI);
$this->assertDatabaseHas('twofaccounts', [
'otp_type' => 'totp',
'legacy_uri' => self::TOTP_SHORT_URI,
'service' => null,
'account' => self::ACCOUNT,
'secret' => self::SECRET,
'digits' => self::DIGITS_DEFAULT,
'period' => self::PERIOD_DEFAULT,
'counter' => null,
'algorithm' => self::ALGORITHM_DEFAULT,
'icon' => null,
]);
}
/**
* @test
*/
public function test_create_from_uri_does_not_persist_to_db()
{
$twofaccount = $this->twofaccountService->createFromUri(self::TOTP_SHORT_URI, false);
$this->assertDatabaseMissing('twofaccounts', [
'otp_type' => 'totp',
'legacy_uri' => self::TOTP_SHORT_URI,
'service' => null,
'account' => self::ACCOUNT,
'secret' => self::SECRET,
'digits' => self::DIGITS_DEFAULT,
'period' => self::PERIOD_DEFAULT,
'counter' => null,
'algorithm' => self::ALGORITHM_DEFAULT,
'icon' => null,
]);
}
/**
* @test
*/
public function test_create_from_invalid_uri_returns_ValidationException()
{
$this->expectException(\Illuminate\Validation\ValidationException::class);
$twofaccount = $this->twofaccountService->createFromUri(self::INVALID_OTPAUTH_URI);
}
/**
* @test
*/
public function test_create_from_uri_without_label_returns_ValidationException()
{
$this->expectException(\Illuminate\Validation\ValidationException::class);
$twofaccount = $this->twofaccountService->createFromUri('otpauth://totp/?secret='.self::SECRET);
}
/**
* @test
*/
public function test_create_custom_totp_from_parameters_returns_correct_value()
{
$twofaccount = $this->twofaccountService->createFromParameters(self::ARRAY_OF_FULL_VALID_PARAMETERS_FOR_CUSTOM_TOTP);
$this->assertEquals('totp', $twofaccount->otp_type);
$this->assertEquals(self::SERVICE, $twofaccount->service);
$this->assertEquals(self::ACCOUNT, $twofaccount->account);
$this->assertEquals(self::SECRET, $twofaccount->secret);
$this->assertEquals(self::DIGITS_CUSTOM, $twofaccount->digits);
$this->assertEquals(self::PERIOD_CUSTOM, $twofaccount->period);
$this->assertEquals(null, $twofaccount->counter);
$this->assertEquals(self::ALGORITHM_CUSTOM, $twofaccount->algorithm);
$this->assertStringEndsWith('.png',$twofaccount->icon);
}
/**
* @test
*/
public function test_create_basic_totp_from_parameters_returns_correct_value()
{
$twofaccount = $this->twofaccountService->createFromParameters(self::ARRAY_OF_MINIMUM_VALID_PARAMETERS_FOR_TOTP);
$this->assertEquals('totp', $twofaccount->otp_type);
$this->assertEquals(null, $twofaccount->service);
$this->assertEquals(self::ACCOUNT, $twofaccount->account);
$this->assertEquals(self::SECRET, $twofaccount->secret);
$this->assertEquals(self::DIGITS_DEFAULT, $twofaccount->digits);
$this->assertEquals(self::PERIOD_DEFAULT, $twofaccount->period);
$this->assertEquals(null, $twofaccount->counter);
$this->assertEquals(self::ALGORITHM_DEFAULT, $twofaccount->algorithm);
$this->assertEquals(null, $twofaccount->icon);
}
/**
* @test
*/
public function test_create_custom_hotp_from_parameters_returns_correct_value()
{
$twofaccount = $this->twofaccountService->createFromParameters(self::ARRAY_OF_FULL_VALID_PARAMETERS_FOR_CUSTOM_HOTP);
$this->assertEquals('hotp', $twofaccount->otp_type);
$this->assertEquals(self::SERVICE, $twofaccount->service);
$this->assertEquals(self::ACCOUNT, $twofaccount->account);
$this->assertEquals(self::SECRET, $twofaccount->secret);
$this->assertEquals(self::DIGITS_CUSTOM, $twofaccount->digits);
$this->assertEquals(null, $twofaccount->period);
$this->assertEquals(self::COUNTER_CUSTOM, $twofaccount->counter);
$this->assertEquals(self::ALGORITHM_CUSTOM, $twofaccount->algorithm);
$this->assertStringEndsWith('.png',$twofaccount->icon);
}
/**
* @test
*/
public function test_create_basic_hotp_from_parameters_returns_correct_value()
{
$twofaccount = $this->twofaccountService->createFromParameters(self::ARRAY_OF_MINIMUM_VALID_PARAMETERS_FOR_HOTP);
$this->assertEquals('hotp', $twofaccount->otp_type);
$this->assertEquals(null, $twofaccount->service);
$this->assertEquals(self::ACCOUNT, $twofaccount->account);
$this->assertEquals(self::SECRET, $twofaccount->secret);
$this->assertEquals(self::DIGITS_DEFAULT, $twofaccount->digits);
$this->assertEquals(null, $twofaccount->period);
$this->assertEquals(self::COUNTER_DEFAULT, $twofaccount->counter);
$this->assertEquals(self::ALGORITHM_DEFAULT, $twofaccount->algorithm);
$this->assertEquals(null, $twofaccount->icon);
}
/**
* @test
*/
public function test_create_from_parameters_persists_to_db()
{
$twofaccount = $this->twofaccountService->createFromParameters(self::ARRAY_OF_MINIMUM_VALID_PARAMETERS_FOR_TOTP);
$this->assertDatabaseHas('twofaccounts', [
'otp_type' => 'totp',
'legacy_uri' => self::TOTP_SHORT_URI,
'service' => null,
'account' => self::ACCOUNT,
'secret' => self::SECRET,
'digits' => self::DIGITS_DEFAULT,
'period' => self::PERIOD_DEFAULT,
'counter' => null,
'algorithm' => self::ALGORITHM_DEFAULT,
'icon' => null,
]);
}
/**
* @test
*/
public function test_create_from_parameters_does_not_persist_to_db()
{
$twofaccount = $this->twofaccountService->createFromParameters(self::ARRAY_OF_MINIMUM_VALID_PARAMETERS_FOR_TOTP, false);
$this->assertDatabaseMissing('twofaccounts', [
'otp_type' => 'totp',
'legacy_uri' => self::TOTP_SHORT_URI,
'service' => null,
'account' => self::ACCOUNT,
'secret' => self::SECRET,
'digits' => self::DIGITS_DEFAULT,
'period' => self::PERIOD_DEFAULT,
'counter' => null,
'algorithm' => self::ALGORITHM_DEFAULT,
'icon' => null,
]);
}
/**
* @test
*/
public function test_create_from_unsupported_parameters_returns_ValidationException()
{
$this->expectException(\Illuminate\Validation\ValidationException::class);
$twofaccount = $this->twofaccountService->createFromParameters(self::ARRAY_OF_PARAMETERS_FOR_UNSUPPORTED_OTP_TYPE);
}
/**
* @test
*/
public function test_create_from_invalid_parameters_type_returns_InvalidOtpParameterException()
{
$this->expectException(\App\Exceptions\InvalidOtpParameterException::class);
$twofaccount = $this->twofaccountService->createFromParameters([
'account' => self::ACCOUNT,
'otp_type' => 'totp',
'digits' => 'notsupported',
]);
}
/**
* @test
*/
public function test_create_from_invalid_parameters_returns_InvalidOtpParameterException()
{
$this->expectException(\App\Exceptions\InvalidOtpParameterException::class);
$twofaccount = $this->twofaccountService->createFromParameters([
'account' => self::ACCOUNT,
'otp_type' => 'totp',
'algorithm' => 'notsupported',
]);
}
/**
* @test
*/
public function test_update_totp_returns_updated_model()
{
$twofaccount = $this->twofaccountService->update($this->customTotpTwofaccount, self::ARRAY_OF_MINIMUM_VALID_PARAMETERS_FOR_TOTP);
$this->assertEquals('totp', $twofaccount->otp_type);
$this->assertEquals(null, $twofaccount->service);
$this->assertEquals(self::ACCOUNT, $twofaccount->account);
$this->assertEquals(self::SECRET, $twofaccount->secret);
$this->assertEquals(self::DIGITS_DEFAULT, $twofaccount->digits);
$this->assertEquals(self::PERIOD_DEFAULT, $twofaccount->period);
$this->assertEquals(null, $twofaccount->counter);
$this->assertEquals(self::ALGORITHM_DEFAULT, $twofaccount->algorithm);
$this->assertEquals(null, $twofaccount->counter);
$this->assertEquals(null, $twofaccount->icon);
}
/**
* @test
*/
public function test_update_hotp_returns_updated_model()
{
$twofaccount = $this->twofaccountService->update($this->customTotpTwofaccount, self::ARRAY_OF_MINIMUM_VALID_PARAMETERS_FOR_HOTP);
$this->assertEquals('hotp', $twofaccount->otp_type);
$this->assertEquals(null, $twofaccount->service);
$this->assertEquals(self::ACCOUNT, $twofaccount->account);
$this->assertEquals(self::SECRET, $twofaccount->secret);
$this->assertEquals(self::DIGITS_DEFAULT, $twofaccount->digits);
$this->assertEquals(null, $twofaccount->period);
$this->assertEquals(self::COUNTER_DEFAULT, $twofaccount->counter);
$this->assertEquals(self::ALGORITHM_DEFAULT, $twofaccount->algorithm);
$this->assertEquals(null, $twofaccount->counter);
$this->assertEquals(null, $twofaccount->icon);
}
/**
* @test
*/
public function test_update_totp_persists_updated_model()
{
$twofaccount = $this->twofaccountService->update($this->customTotpTwofaccount, self::ARRAY_OF_MINIMUM_VALID_PARAMETERS_FOR_TOTP);
$this->assertDatabaseHas('twofaccounts', [
'otp_type' => 'totp',
'service' => null,
'account' => self::ACCOUNT,
'secret' => self::SECRET,
'digits' => self::DIGITS_DEFAULT,
'period' => self::PERIOD_DEFAULT,
'counter' => null,
'algorithm' => self::ALGORITHM_DEFAULT,
'icon' => null,
]);
}
/**
* @test
*/
public function test_getOTP_for_totp_returns_the_same_password()
{
$otp_from_model = $this->twofaccountService->getOTP($this->customTotpTwofaccount);
$otp_from_id = $this->twofaccountService->getOTP($this->customTotpTwofaccount->id);
$otp_from_uri = $this->twofaccountService->getOTP(self::TOTP_FULL_CUSTOM_URI);
// Those assertions may fail if the 3 previous assignments are not done at the same exact timestamp
$this->assertEquals($otp_from_model, $otp_from_id);
$this->assertEquals($otp_from_model, $otp_from_uri);
}
/**
* @test
*/
public function test_getOTP_for_hotp_returns_the_same_password()
{
$otp_from_model = $this->twofaccountService->getOTP($this->customHotpTwofaccount);
$otp_from_id = $this->twofaccountService->getOTP($this->customHotpTwofaccount->id);
$otp_from_uri = $this->twofaccountService->getOTP(self::HOTP_FULL_CUSTOM_URI);
// Those assertions may fail if the 3 previous assignments are not done at the same exact timestamp
$this->assertEquals($otp_from_model, $otp_from_id);
$this->assertEquals($otp_from_model, $otp_from_uri);
}
/**
* @test
*/
public function test_getOTP_for_totp_with_invalid_secret_returns_InvalidSecretException()
{
$this->expectException(\App\Exceptions\InvalidSecretException::class);
$otp_from_uri = $this->twofaccountService->getOTP('otpauth://totp/'.self::ACCOUNT.'?secret=0');
}
/**
* @test
*/
public function test_getURI_for_custom_totp_model_returns_uri()
{
$uri = $this->twofaccountService->getURI($this->customTotpTwofaccount);
$this->assertStringContainsString('otpauth://totp/', $uri);
$this->assertStringContainsString(self::SERVICE, $uri);
$this->assertStringContainsString(self::ACCOUNT, $uri);
$this->assertStringContainsString('secret='.self::SECRET, $uri);
$this->assertStringContainsString('digits='.self::DIGITS_CUSTOM, $uri);
$this->assertStringContainsString('period='.self::PERIOD_CUSTOM, $uri);
$this->assertStringContainsString('algorithm='.self::ALGORITHM_CUSTOM, $uri);
}
/**
* @test
*/
public function test_getURI_for_custom_totp_model_id_returns_uri()
{
$uri = $this->twofaccountService->getURI($this->customTotpTwofaccount->id);
$this->assertStringContainsString('otpauth://totp/', $uri);
$this->assertStringContainsString(self::SERVICE, $uri);
$this->assertStringContainsString(self::ACCOUNT, $uri);
$this->assertStringContainsString('secret='.self::SECRET, $uri);
$this->assertStringContainsString('digits='.self::DIGITS_CUSTOM, $uri);
$this->assertStringContainsString('period='.self::PERIOD_CUSTOM, $uri);
$this->assertStringContainsString('algorithm='.self::ALGORITHM_CUSTOM, $uri);
}
/**
* @test
*/
public function test_getURI_for_custom_hotp_model_returns_uri()
{
$uri = $this->twofaccountService->getURI($this->customHotpTwofaccount);
$this->assertStringContainsString('otpauth://hotp/', $uri);
$this->assertStringContainsString(self::SERVICE, $uri);
$this->assertStringContainsString(self::ACCOUNT, $uri);
$this->assertStringContainsString('secret='.self::SECRET, $uri);
$this->assertStringContainsString('digits='.self::DIGITS_CUSTOM, $uri);
$this->assertStringContainsString('counter='.self::COUNTER_CUSTOM, $uri);
$this->assertStringContainsString('algorithm='.self::ALGORITHM_CUSTOM, $uri);
}
/**
* @test
*/
public function test_getURI_for_custom_hotp_model_id_returns_uri()
{
$uri = $this->twofaccountService->getURI($this->customHotpTwofaccount->id);
$this->assertStringContainsString('otpauth://hotp/', $uri);
$this->assertStringContainsString(self::SERVICE, $uri);
$this->assertStringContainsString(self::ACCOUNT, $uri);
$this->assertStringContainsString('secret='.self::SECRET, $uri);
$this->assertStringContainsString('digits='.self::DIGITS_CUSTOM, $uri);
$this->assertStringContainsString('counter='.self::COUNTER_CUSTOM, $uri);
$this->assertStringContainsString('algorithm='.self::ALGORITHM_CUSTOM, $uri);
}
/**
* @test
*/
public function test_getURI_for_totp_dto_returns_uri()
{
$dto = new \App\Services\Dto\TwoFAccountDto;
$dto->otp_type = 'totp';
$dto->account = self::ACCOUNT;
$dto->secret = self::SECRET;
$uri = $this->twofaccountService->getURI($dto);
$this->assertStringContainsString('otpauth://totp/', $uri);
$this->assertStringContainsString(self::ACCOUNT, $uri);
$this->assertStringContainsString('secret='.self::SECRET, $uri);
}
}

View File

@ -1,109 +0,0 @@
<?php
namespace Tests\Unit;
use App\User;
use Tests\TestCase;
use App\TwoFAccount;
use App\Http\Controllers\TwoFAccountController;
use Illuminate\Auth\Authenticatable;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Hash;
use Illuminate\Auth\RequestGuard;
use Symfony\Component\HttpKernel\Exception\HttpException;
class ApiExceptionTest extends TestCase
{
/** @var \App\User */
protected $user;
/**
* @test
*/
public function setUp(): void
{
parent::setUp();
$this->user = factory(User::class)->create();
}
/**
* test Unauthorized
*
* @test
*/
public function testHttpUnauthenticated()
{
$response = $this->json('GET', '/api/settings/options')
->assertStatus(401)
->assertJson([
'message' => 'Unauthenticated.'
]);
}
/**
* test Not Found
*
* @test
*/
public function testHttpNotFound()
{
$response = $this->actingAs($this->user, 'api')
->json('GET', '/api/twofaccounts/1000')
->assertStatus(404);
}
/**
* test Method Not Allowed
*
* @test
*/
public function testHttpMethodNotAllowed()
{
$response = $this->actingAs($this->user, 'api')
->json('PATCH', '/api/settings/options')
->assertStatus(405);
}
/**
* test Unprocessable entity
*
* @test
*/
public function testHttpUnprocessableEntity()
{
$response = $this->json('POST', '/api/login')
->assertStatus(422)
->assertJsonStructure([
'message',
'errors'
])
->assertJsonValidationErrors([
'email',
'password'
]);
}
/**
* test Internal Server error
*
* @test
*/
public function testHttpInternalServerError()
{
factory(TwoFAccount::class, 3)->create();
$response = $this->actingAs($this->user, 'api')
->json('PATCH', '/api/twofaccounts/reorder', [
'orderedIds' => 'x'])
->assertStatus(500);
}
}

View File

@ -1,84 +0,0 @@
<?php
namespace Tests\Unit\Settings;
use App\User;
use Tests\TestCase;
class AccountTest extends TestCase
{
/** @var \App\User */
protected $user;
/**
* @test
*/
public function setUp(): void
{
parent::setUp();
$this->user = factory(User::class)->create();
}
/**
* test Get user infos via API
*
* @test
*/
public function testGetUserDetails()
{
$user = User::find(1);
$response = $this->actingAs($user, 'api')
->json('GET', '/api/settings/account')
->assertStatus(200)
->assertJsonStructure(['name', 'email']);
}
/**
* test User update with wrong current password via API
*
* @test
*/
public function testUserUpdateWithWrongCurrentPassword()
{
$user = User::find(1);
$response = $this->actingAs($user, 'api')
->json('PATCH', '/api/settings/account', [
'name' => 'userUpdated',
'email' => 'userUpdated@example.org',
'password' => 'wrongPassword',
]);
$response->assertStatus(400)
->assertJsonStructure(['message']);
}
/**
* test User update via API
*
* @test
*/
public function testUserUpdate()
{
$user = User::find(1);
$response = $this->actingAs($user, 'api')
->json('PATCH', '/api/settings/account', [
'name' => 'userUpdated',
'email' => 'userUpdated@example.org',
'password' => 'password',
]);
$response->assertStatus(200)
->assertJsonFragment([
'username' => 'userUpdated'
]);
}
}

View File

@ -1,67 +0,0 @@
<?php
namespace Tests\Unit\Settings;
use App\User;
use Tests\TestCase;
use Illuminate\Support\Facades\Hash;
class PasswordTest extends TestCase
{
/** @var \App\User */
protected $user;
/**
* @test
*/
public function setUp(): void
{
parent::setUp();
$this->user = factory(User::class)->create();
}
/**
* test User password update with wrong current password via API
*
* @test
*/
public function testPasswordUpdateWithWrongCurrentPassword()
{
$response = $this->actingAs($this->user, 'api')
->json('PATCH', '/api/settings/password', [
'currentPassword' => 'wrongPassword',
'password' => 'passwordUpdated',
'password_confirmation' => 'passwordUpdated',
]);
$response->assertStatus(400)
->assertJsonStructure(['message']);
}
/**
* test User password update via API
*
* @test
*/
public function testPasswordUpdate()
{
$response = $this->actingAs($this->user, 'api')
->json('PATCH', '/api/settings/password', [
'currentPassword' => 'password',
'password' => 'passwordUpdated',
'password_confirmation' => 'passwordUpdated',
]);
$response->assertStatus(200)
->assertJsonStructure(['message']);
$this->user->refresh();
$this->assertTrue(Hash::check('passwordUpdated', $this->user->password));
}
}