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));
- }
-
-}