diff --git a/app/Services/TwoFAccountService.php b/app/Services/TwoFAccountService.php index c52a6032..fcf01141 100644 --- a/app/Services/TwoFAccountService.php +++ b/app/Services/TwoFAccountService.php @@ -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; diff --git a/composer.json b/composer.json index 507d779c..f7028aee 100644 --- a/composer.json +++ b/composer.json @@ -69,6 +69,10 @@ ], "test" : [ "vendor/bin/phpunit" + ], + "test-coverage-html" : [ + "@putenv XDEBUG_MODE=coverage", + "vendor/bin/phpunit --coverage-html tests/Coverage/" ] } } diff --git a/phpunit.xml b/phpunit.xml index 7275a75b..3e8d67fb 100644 --- a/phpunit.xml +++ b/phpunit.xml @@ -23,6 +23,9 @@ ./tests/Feature + + ./tests/Api/v1 + diff --git a/tests/Api/v1/Controllers/Auth/ForgotPasswordControllerTest.php b/tests/Api/v1/Controllers/Auth/ForgotPasswordControllerTest.php new file mode 100644 index 00000000..eec1e11d --- /dev/null +++ b/tests/Api/v1/Controllers/Auth/ForgotPasswordControllerTest.php @@ -0,0 +1,95 @@ +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); + } + +} \ No newline at end of file diff --git a/tests/Api/v1/Controllers/Auth/PasswordControllerTest.php b/tests/Api/v1/Controllers/Auth/PasswordControllerTest.php new file mode 100644 index 00000000..9915109c --- /dev/null +++ b/tests/Api/v1/Controllers/Auth/PasswordControllerTest.php @@ -0,0 +1,81 @@ +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); + } + +} \ No newline at end of file diff --git a/tests/Api/v1/Controllers/Auth/RegisterControllerTest.php b/tests/Api/v1/Controllers/Auth/RegisterControllerTest.php new file mode 100644 index 00000000..208ba781 --- /dev/null +++ b/tests/Api/v1/Controllers/Auth/RegisterControllerTest.php @@ -0,0 +1,59 @@ +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); + } + +} \ No newline at end of file diff --git a/tests/Feature/Auth/ResetPasswordTest.php b/tests/Api/v1/Controllers/Auth/ResetPasswordControllerTest.php similarity index 56% rename from tests/Feature/Auth/ResetPasswordTest.php rename to tests/Api/v1/Controllers/Auth/ResetPasswordControllerTest.php index 89763956..c509b6ae 100644 --- a/tests/Feature/Auth/ResetPasswordTest.php +++ b/tests/Api/v1/Controllers/Auth/ResetPasswordControllerTest.php @@ -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)); } diff --git a/tests/Api/v1/Controllers/Auth/UserControllerTest.php b/tests/Api/v1/Controllers/Auth/UserControllerTest.php new file mode 100644 index 00000000..c39b2d79 --- /dev/null +++ b/tests/Api/v1/Controllers/Auth/UserControllerTest.php @@ -0,0 +1,144 @@ +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); + } + +} \ No newline at end of file diff --git a/tests/Api/v1/Controllers/GroupControllerTest.php b/tests/Api/v1/Controllers/GroupControllerTest.php index c97fca82..d6054d16 100644 --- a/tests/Api/v1/Controllers/GroupControllerTest.php +++ b/tests/Api/v1/Controllers/GroupControllerTest.php @@ -7,6 +7,10 @@ use Tests\FeatureTestCase; use App\TwoFAccount; + +/** + * @covers \App\Api\v1\Controllers\GroupController + */ class GroupControllerTest extends FeatureTestCase { /** diff --git a/tests/Api/v1/Controllers/IconControllerTest.php b/tests/Api/v1/Controllers/IconControllerTest.php index 36ca8047..31239220 100644 --- a/tests/Api/v1/Controllers/IconControllerTest.php +++ b/tests/Api/v1/Controllers/IconControllerTest.php @@ -6,6 +6,10 @@ use Illuminate\Foundation\Testing\WithoutMiddleware; use Tests\TestCase; + +/** + * @covers \App\Api\v1\Controllers\IconController + */ class IconControllerTest extends TestCase { diff --git a/tests/Api/v1/Controllers/QrcodeControllerTest.php b/tests/Api/v1/Controllers/QrCodeControllerTest.php similarity index 96% rename from tests/Api/v1/Controllers/QrcodeControllerTest.php rename to tests/Api/v1/Controllers/QrCodeControllerTest.php index acdc12c5..b56ab928 100644 --- a/tests/Api/v1/Controllers/QrcodeControllerTest.php +++ b/tests/Api/v1/Controllers/QrCodeControllerTest.php @@ -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 { /** diff --git a/tests/Api/v1/Controllers/SettingControllerTest.php b/tests/Api/v1/Controllers/SettingControllerTest.php index 8206268f..119404a8 100644 --- a/tests/Api/v1/Controllers/SettingControllerTest.php +++ b/tests/Api/v1/Controllers/SettingControllerTest.php @@ -7,6 +7,10 @@ use Tests\FeatureTestCase; use App\TwoFAccount; + +/** + * @covers \App\Api\v1\Controllers\SettingController + */ class SettingControllerTest extends FeatureTestCase { /** diff --git a/tests/Api/v1/Controllers/TwoFAccountControllerTest.php b/tests/Api/v1/Controllers/TwoFAccountControllerTest.php index ec6820d0..74acfb6a 100644 --- a/tests/Api/v1/Controllers/TwoFAccountControllerTest.php +++ b/tests/Api/v1/Controllers/TwoFAccountControllerTest.php @@ -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 */ diff --git a/tests/Feature/AccountsGroupTest.php b/tests/Feature/AccountsGroupTest.php deleted file mode 100644 index 2d29d7ad..00000000 --- a/tests/Feature/AccountsGroupTest.php +++ /dev/null @@ -1,261 +0,0 @@ -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 - ] - ); - - } - -} \ No newline at end of file diff --git a/tests/Feature/Auth/ForgotPasswordTest.php b/tests/Feature/Auth/ForgotPasswordTest.php deleted file mode 100644 index 72e1ee54..00000000 --- a/tests/Feature/Auth/ForgotPasswordTest.php +++ /dev/null @@ -1,103 +0,0 @@ -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); - } - -} \ No newline at end of file diff --git a/tests/Feature/Auth/LoginTest.php b/tests/Feature/Auth/LoginTest.php index 4b280a12..81b22004 100644 --- a/tests/Feature/Auth/LoginTest.php +++ b/tests/Feature/Auth/LoginTest.php @@ -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(); } } \ No newline at end of file diff --git a/tests/Feature/Auth/RegisterTest.php b/tests/Feature/Auth/RegisterTest.php deleted file mode 100644 index bb9172d1..00000000 --- a/tests/Feature/Auth/RegisterTest.php +++ /dev/null @@ -1,124 +0,0 @@ -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'] - ]); - } - -} diff --git a/tests/Feature/ProtectDbTest.php b/tests/Feature/ProtectDbTest.php deleted file mode 100644 index 5e81cd92..00000000 --- a/tests/Feature/ProtectDbTest.php +++ /dev/null @@ -1,288 +0,0 @@ -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*', - ]); - } - -} \ No newline at end of file diff --git a/tests/Unit/RouteTest.php b/tests/Feature/RouteTest.php similarity index 66% rename from tests/Unit/RouteTest.php rename to tests/Feature/RouteTest.php index 83f31ed2..29779d45 100644 --- a/tests/Unit/RouteTest.php +++ b/tests/Feature/RouteTest.php @@ -1,10 +1,10 @@ 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('/'); diff --git a/tests/Feature/Services/AppstractOptionsServiceTest.php b/tests/Feature/Services/AppstractOptionsServiceTest.php new file mode 100644 index 00000000..84821322 --- /dev/null +++ b/tests/Feature/Services/AppstractOptionsServiceTest.php @@ -0,0 +1,197 @@ +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 + ]); + } +} \ No newline at end of file diff --git a/tests/Feature/Services/TwoFAccountServiceTest.php b/tests/Feature/Services/TwoFAccountServiceTest.php new file mode 100644 index 00000000..881a5653 --- /dev/null +++ b/tests/Feature/Services/TwoFAccountServiceTest.php @@ -0,0 +1,621 @@ + 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); + } + +} \ No newline at end of file diff --git a/tests/Unit/ApiExceptionTest.php b/tests/Unit/ApiExceptionTest.php deleted file mode 100644 index 8d07e6b3..00000000 --- a/tests/Unit/ApiExceptionTest.php +++ /dev/null @@ -1,109 +0,0 @@ -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); - - } - -} \ No newline at end of file diff --git a/tests/Unit/Settings/AccountTest.php b/tests/Unit/Settings/AccountTest.php deleted file mode 100644 index 4bb2567a..00000000 --- a/tests/Unit/Settings/AccountTest.php +++ /dev/null @@ -1,84 +0,0 @@ -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' - ]); - } - -} \ No newline at end of file diff --git a/tests/Unit/Settings/PasswordTest.php b/tests/Unit/Settings/PasswordTest.php deleted file mode 100644 index f122fa0c..00000000 --- a/tests/Unit/Settings/PasswordTest.php +++ /dev/null @@ -1,67 +0,0 @@ -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)); - } - -}