diff --git a/src/Facades/Identity.php b/src/Facades/Identity.php index 8796a4d..87e4eab 100644 --- a/src/Facades/Identity.php +++ b/src/Facades/Identity.php @@ -93,7 +93,8 @@ public static function events() */ public static function findUserByIdOrFail(string $id) { - return static::newUserModel()->where('id', $id)->firstOrFail(); + $model = static::newUserModel(); + return $model->where($model->getKeyName(), $id)->firstOrFail(); } /** diff --git a/src/Providers/IdentitiesServiceProvider.php b/src/Providers/IdentitiesServiceProvider.php index 9d3800d..b725ab3 100644 --- a/src/Providers/IdentitiesServiceProvider.php +++ b/src/Providers/IdentitiesServiceProvider.php @@ -38,7 +38,9 @@ public function register() $config = $app->make('config')->get('identities'); $app_config = $app->make('config')->get('app'); - if (Str::startsWith($key = $config['key'], 'base64:')) { + $key = $config['key'] ?? $app_config['key']; + + if (Str::startsWith($key, 'base64:')) { $key = base64_decode(substr($key, 7)); } diff --git a/src/Support/FindIdentity.php b/src/Support/FindIdentity.php index 3499531..e77ba9d 100644 --- a/src/Support/FindIdentity.php +++ b/src/Support/FindIdentity.php @@ -9,6 +9,10 @@ trait FindIdentity { /** + * Search a user given its identity in the third party provider + * + * @param \Laravel\Socialite\Contracts\User $identity Third party identity provided by Laravel Socialite + * @param string $provider The identity provider name * @return \Illuminate\Contracts\Auth\Authenticatable|null */ protected function findUserFromIdentity($identity, $provider) diff --git a/tests/Fixtures/Concern/UseTestFixtures.php b/tests/Fixtures/Concern/UseTestFixtures.php new file mode 100644 index 0000000..3c2ad7b --- /dev/null +++ b/tests/Fixtures/Concern/UseTestFixtures.php @@ -0,0 +1,16 @@ +setUpDatabase(); + + $this->setUpSession($this->app); + $this->activateSocialiteExtensions(); } + protected function setUpTraits() + { + parent::setUpTraits(); + + $uses = \array_flip(\class_uses_recursive(static::class)); + + if (isset($uses[UseTestFixtures::class])) { + $this->useTestFixtures(); + } + + return $uses; + } + /** * Define environment setup. * @@ -55,9 +75,9 @@ protected function getEnvironmentSetUp($app) ]); $key = Str::random(32); - $app['config']->set('app.key', $key); + $app['config']->set('app.key', 'base64:'.base64_encode($key)); $app['config']->set('app.cipher', 'AES-256-CBC'); - $app['config']->set('identities.key', $key); + $app['config']->set('identities.key', 'base64:'.base64_encode($key)); } /** @@ -72,6 +92,23 @@ protected function getPackageProviders($app) ]; } + protected function setUpDatabase() + { + $this->loadLaravelMigrations(); + + $this->loadMigrationsFrom(__DIR__.'/../stubs/migrations'); + + IdentityFacade::useNamespace("App"); + IdentityFacade::useIdentityModel("App\\Identity"); + IdentityFacade::useUserModel("App\\User"); + } + + protected function setUpSession() + { + $kernel = $this->app->make('Illuminate\Contracts\Http\Kernel'); + $kernel->pushMiddleware('Illuminate\Session\Middleware\StartSession'); + } + protected function activateSocialiteExtensions() { $socialiteWasCalled = $this->app->make(SocialiteWasCalled::class); @@ -97,4 +134,16 @@ public function assertListenerIsAttachedToEvent($listener, $event) $this->assertTrue(false, sprintf('Event %s does not have the %s listener attached to it', $event, $listener)); } + + public function createUser($data = []) + { + return tap((new User), function ($u) use ($data) { + $u->forceFill(array_merge([ + 'email' => 'user@local.com', + 'name' => 'User', + 'password' => '$2y$10$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi', + 'remember_token' => Str::random(10), + ], $data))->save(); + }); + } } diff --git a/tests/Unit/ConnectControllerTest.php b/tests/Unit/ConnectControllerTest.php new file mode 100644 index 0000000..9e9e4d1 --- /dev/null +++ b/tests/Unit/ConnectControllerTest.php @@ -0,0 +1,159 @@ +app->make('router'); + + $router->get('login', function () { + })->name('login'); + + $this->withoutExceptionHandling(); + + $this->expectException(AuthenticationException::class); + $this->expectExceptionMessage('Unauthenticated'); + + $this->get(route('oneofftech::connect.provider', ['provider' => 'gitlab'])); + } + + public function test_redirect_to_provider() + { + IdentityFacade::useNamespace('Tests\\Fixtures'); + IdentityFacade::routes(); + + $user = tap((new User()), function ($u) { + $u->forceFill([ + 'email' => 'user@local.local', + 'name' => 'User', + 'password' => '$2y$10$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi', + 'remember_token' => Str::random(10), + ])->save(); + }); + + $response = $this->actingAs($user) + ->get(route('oneofftech::connect.provider', ['provider' => 'gitlab'])); + + $response->assertRedirect(); + + $location = urldecode($response->headers->get('Location')); + + $this->assertStringContainsString('gitlab.com', $location); + $this->assertStringContainsString(route('oneofftech::connect.callback', ['provider' => 'gitlab']), $location); + } + + public function test_connect_creates_identity() + { + $this->useTestFixtures(); + + $user = $this->createUser(); + + $this->withoutExceptionHandling(); + + $driverMock = Mockery::mock(Provider::class)->makePartial(); + + Carbon::setTestNow(Carbon::create(2020, 11, 12, 10, 20)); + + $oauthFakeUser = (new OauthUser())->map([ + 'id' => 'U1', + 'nickname' => 'User', + 'name' => 'User', + 'email' => 'user@local.com', + 'avatar' => 'https://gitlab.com', + 'token' => 'T2', + 'refreshToken' => 'RT2', + 'expiresIn' => Carbon::SECONDS_PER_MINUTE, + ]); + + $driverMock->shouldReceive('user')->andReturn($oauthFakeUser); + + $driverMock->shouldReceive('redirectUrl')->andReturn($driverMock); + + IdentityFacade::shouldReceive('driver')->with('gitlab')->andReturn($driverMock); + + $response = $this->actingAs($user) + ->get(route('oneofftech::connect.callback', ['provider' => 'gitlab'])); + + $response->assertRedirect('http://localhost/home'); + + $updatedIdentity = $user->identities->first(); + + $this->assertEquals(IdentityCrypt::hash('U1'), $updatedIdentity->provider_id); + $this->assertEquals('gitlab', $updatedIdentity->provider); + $this->assertEquals(Carbon::create(2020, 11, 12, 10, 21), $updatedIdentity->expires_at); + $this->assertEquals('T2', IdentityCrypt::decryptString($updatedIdentity->token)); + $this->assertEquals('RT2', IdentityCrypt::decryptString($updatedIdentity->refresh_token)); + } + + public function test_connect_updates_existing_identity() + { + $this->useTestFixtures(); + + $user = $this->createUser(); + + $identity = $user->identities()->create([ + 'provider'=> 'gitlab', + 'provider_id'=> IdentityCrypt::hash('U1'), + 'token'=> 'T1', + 'refresh_token'=> null, + 'expires_at'=> null, + 'registration' => true, + ]); + + $this->withoutExceptionHandling(); + + $driverMock = Mockery::mock(Provider::class)->makePartial(); + + Carbon::setTestNow(Carbon::create(2020, 11, 12, 10, 20)); + + $oauthFakeUser = (new OauthUser())->map([ + 'id' => 'U1', + 'nickname' => 'User', + 'name' => 'User', + 'email' => 'user@local.com', + 'avatar' => 'https://gitlab.com', + 'token' => 'T2', + 'refreshToken' => 'RT2', + 'expiresIn' => Carbon::SECONDS_PER_MINUTE, + ]); + + $driverMock->shouldReceive('user')->andReturn($oauthFakeUser); + + $driverMock->shouldReceive('redirectUrl')->andReturn($driverMock); + + IdentityFacade::shouldReceive('driver')->with('gitlab')->andReturn($driverMock); + + $response = $this->actingAs($user) + ->get(route('oneofftech::connect.callback', ['provider' => 'gitlab'])); + + $response->assertRedirect('http://localhost/home'); + + $updatedIdentity = $user->identities->first(); + + $this->assertEquals($identity->provider_id, $updatedIdentity->provider_id); + $this->assertEquals('gitlab', $updatedIdentity->provider); + $this->assertEquals(Carbon::create(2020, 11, 12, 10, 21), $updatedIdentity->expires_at); + $this->assertEquals('T2', IdentityCrypt::decryptString($updatedIdentity->token)); + $this->assertEquals('RT2', IdentityCrypt::decryptString($updatedIdentity->refresh_token)); + } +} diff --git a/tests/Unit/EncrypterTest.php b/tests/Unit/EncrypterTest.php index 02d8dfc..a3841d1 100644 --- a/tests/Unit/EncrypterTest.php +++ b/tests/Unit/EncrypterTest.php @@ -20,9 +20,9 @@ public function test_value_encrypted_with_old_key_can_be_decrypted() { $encrypter = new Encrypter(config('identities.old_key'), 'AES-256-CBC'); - $encryptedWithOldKey = $encrypter->encrypt('test'); + $encryptedWithOldKey = $encrypter->encryptString('test'); - $decrypted = IdentityCrypt::decrypt($encryptedWithOldKey); + $decrypted = IdentityCrypt::decryptString($encryptedWithOldKey); $this->assertEquals('test', $decrypted); } @@ -33,18 +33,18 @@ public function test_value_encrypted_with_old_base64_key_can_be_decrypted() $this->app['config']->set('identities.old_key', 'base64:'.base64_encode($key)); $encrypter = new Encrypter($key, 'AES-256-CBC'); - $encryptedWithOldKey = $encrypter->encrypt('test'); + $encryptedWithOldKey = $encrypter->encryptString('test'); - $decrypted = IdentityCrypt::decrypt($encryptedWithOldKey); + $decrypted = IdentityCrypt::decryptString($encryptedWithOldKey); $this->assertEquals('test', $decrypted); } public function test_value_can_be_encrypted() { - $encrypted = IdentityCrypt::encrypt('test'); + $encrypted = IdentityCrypt::encryptString('test'); - $decrypted = IdentityCrypt::decrypt($encrypted); + $decrypted = IdentityCrypt::decryptString($encrypted); $this->assertEquals('test', $decrypted); } diff --git a/tests/Unit/IdentityServiceProviderTest.php b/tests/Unit/IdentityServiceProviderTest.php index 8531855..7421d1d 100644 --- a/tests/Unit/IdentityServiceProviderTest.php +++ b/tests/Unit/IdentityServiceProviderTest.php @@ -11,12 +11,22 @@ use Illuminate\Foundation\Testing\DatabaseMigrations; use InvalidArgumentException; use Laravel\Socialite\Two\FacebookProvider; +use Oneofftech\Identities\Providers\IdentitiesServiceProvider; use SocialiteProviders\GitLab\Provider as GitlabSocialiteProvider; class IdentityServiceProviderTest extends TestCase { use DatabaseMigrations; + public function test_default_driver_cannot_not_configured() + { + $factory = $this->app->make(IdentitiesManager::class); + + $this->expectException(InvalidArgumentException::class); + + $factory->redirect(); + } + public function test_it_can_instantiate_the_gitlab_driver() { $factory = $this->app->make(IdentitiesManager::class); @@ -101,4 +111,11 @@ public function test_facade_return_manager_instance() { $this->assertInstanceOf(IdentitiesManager::class, Identity::getFacadeRoot()); } + + public function test_provider_lists_provided_services() + { + $provides = (new IdentitiesServiceProvider($this->app))->provides(); + + $this->assertEquals([IdentitiesManager::class], $provides); + } } diff --git a/tests/Unit/IdentityTraitTest.php b/tests/Unit/IdentityTraitTest.php new file mode 100644 index 0000000..4c0c8a4 --- /dev/null +++ b/tests/Unit/IdentityTraitTest.php @@ -0,0 +1,155 @@ +forceFill([ + 'email' => 'user@local.local', + 'name' => 'User', + 'password' => '$2y$10$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi', + 'remember_token' => Str::random(10), + ])->save(); + }); + + $identity = $user->identities()->create([ + 'provider'=> 'social', + 'provider_id'=> 'aaaa', + 'token'=> 'tttt', + 'refresh_token'=> null, + 'expires_at'=> null, + 'registration' => true, + ]); + + $this->assertInstanceOf(Identity::class, $identity); + $this->assertNotNull($identity->getKey()); + $this->assertEquals('social', $identity->provider); + $this->assertEquals('aaaa', $identity->provider_id); + $this->assertEquals('tttt', $identity->token); + $this->assertNull($identity->refresh_token); + $this->assertNull($identity->expires_at); + $this->assertTrue($identity->registration); + $this->assertTrue($identity->user->is($user)); + } + + public function test_find_identity_trait() + { + IdentityFacade::useNamespace("Tests\\Fixtures\\"); + IdentityFacade::useIdentityModel("Tests\\Fixtures\\Identity"); + IdentityFacade::useUserModel("Tests\\Fixtures\\User"); + + $expectedUser = tap((new User), function ($u) { + $u->forceFill([ + 'email' => 'user@local.local', + 'name' => 'User', + 'password' => '$2y$10$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi', + 'remember_token' => Str::random(10), + ])->save(); + + $u->identities()->create([ + 'provider'=> 'social', + 'provider_id'=> IdentityCrypt::hash('P1'), + 'token'=> 'tttt', + 'refresh_token'=> null, + 'expires_at'=> null, + 'registration' => true, + ]); + }); + + $user = $this->findUserFromIdentity(new class implements SocialiteUser { + public function getId() + { + return 'P1'; + } + public function getNickname() + { + return null; + } + public function getName() + { + return null; + } + public function getEmail() + { + return null; + } + public function getAvatar() + { + return null; + } + }, 'social'); + + $this->assertTrue($expectedUser->is($user)); + } + + public function test_find_identity_trait_return_null_when_not_found() + { + IdentityFacade::useNamespace("Tests\\Fixtures\\"); + IdentityFacade::useIdentityModel("Tests\\Fixtures\\Identity"); + IdentityFacade::useUserModel("Tests\\Fixtures\\User"); + + $user = $this->findUserFromIdentity(new class implements SocialiteUser { + public function getId() + { + return 'P1'; + } + public function getNickname() + { + return null; + } + public function getName() + { + return null; + } + public function getEmail() + { + return null; + } + public function getAvatar() + { + return null; + } + }, 'social'); + + $this->assertNull($user); + } + + public function test_find_user() + { + IdentityFacade::useNamespace("Tests\\Fixtures\\"); + IdentityFacade::useIdentityModel("Tests\\Fixtures\\Identity"); + IdentityFacade::useUserModel("Tests\\Fixtures\\User"); + + $expectedUser = tap((new User), function ($u) { + $u->forceFill([ + 'email' => 'user@local.local', + 'name' => 'User', + 'password' => '$2y$10$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi', + 'remember_token' => Str::random(10), + ])->save(); + }); + + $user = IdentityFacade::findUserByIdOrFail($expectedUser->getKey()); + + $this->assertTrue($expectedUser->is($user)); + } +} diff --git a/tests/Unit/InteractsWithAdditionalAttributesDefinedViaMethodTest.php b/tests/Unit/InteractsWithAdditionalAttributesDefinedViaMethodTest.php new file mode 100644 index 0000000..af39aa5 --- /dev/null +++ b/tests/Unit/InteractsWithAdditionalAttributesDefinedViaMethodTest.php @@ -0,0 +1,69 @@ +redirectAttributes(); + + $this->assertEquals(['attribute'], $attributes); + } + + public function test_attributes_are_saved() + { + Session::shouldReceive('put')->once()->with('_oot.identities.attributes', '{"attribute":"value"}'); + + $request = Request::create('http://localhost', 'GET', [ + 'attribute' => 'value' + ]); + $request->setLaravelSession(Session::getFacadeRoot()); + + $this->pushAttributes($request); + } + + public function test_attributes_are_retrieved() + { + Session::shouldReceive('previousUrl')->andReturnNull(); + + Session::shouldReceive('pull')->once() + ->with('_oot.identities.attributes') + ->andReturn('{"attribute":"http://localhost/previous"}'); + + $request = Request::create('http://localhost/callback'); + $request->setLaravelSession(Session::getFacadeRoot()); + + $data = $this->pullAttributes($request); + + $this->assertEquals(['attribute' => 'http://localhost/previous'], $data); + } + + public function test_attributes_not_retrieved_if_nothing_saved() + { + Session::shouldReceive('previousUrl')->andReturnNull(); + + Session::shouldReceive('pull')->once() + ->with('_oot.identities.attributes') + ->andReturn(null); + + $request = Request::create('http://localhost/callback'); + $request->setLaravelSession(Session::getFacadeRoot()); + + $data = $this->pullAttributes($request); + + $this->assertEquals([], $data); + } +} diff --git a/tests/Unit/InteractsWithAdditionalAttributesDefinedViaPropertyTest.php b/tests/Unit/InteractsWithAdditionalAttributesDefinedViaPropertyTest.php new file mode 100644 index 0000000..35d6c5f --- /dev/null +++ b/tests/Unit/InteractsWithAdditionalAttributesDefinedViaPropertyTest.php @@ -0,0 +1,50 @@ +redirectAttributes(); + + $this->assertEquals(['attribute'], $attributes); + } + + public function test_attributes_are_saved() + { + Session::shouldReceive('put')->once()->with('_oot.identities.attributes', '{"attribute":"value"}'); + + $request = Request::create('http://localhost', 'GET', [ + 'attribute' => 'value' + ]); + $request->setLaravelSession(Session::getFacadeRoot()); + + $this->pushAttributes($request); + } + + public function test_attributes_are_retrieved() + { + Session::shouldReceive('previousUrl')->andReturnNull(); + + Session::shouldReceive('pull')->once() + ->with('_oot.identities.attributes') + ->andReturn('{"attribute":"http://localhost/previous"}'); + + $request = Request::create('http://localhost/callback'); + $request->setLaravelSession(Session::getFacadeRoot()); + + $data = $this->pullAttributes($request); + + $this->assertEquals(['attribute' => 'http://localhost/previous'], $data); + } +} diff --git a/tests/Unit/InteractsWithAdditionalAttributesTest.php b/tests/Unit/InteractsWithAdditionalAttributesTest.php new file mode 100644 index 0000000..1b70a86 --- /dev/null +++ b/tests/Unit/InteractsWithAdditionalAttributesTest.php @@ -0,0 +1,46 @@ +redirectAttributes(); + + $this->assertEquals([], $attributes); + } + + public function test_nothing_is_saved() + { + Session::shouldReceive('put')->never(); + + $request = Request::create('http://localhost', 'GET', [ + 'attribute' => 'value' + ]); + $request->setLaravelSession(Session::getFacadeRoot()); + + $this->pushAttributes($request); + } + + public function test_nothing_is_retrieved() + { + Session::shouldReceive('previousUrl')->andReturnNull(); + + Session::shouldReceive('pull')->never(); + + $request = Request::create('http://localhost/callback'); + $request->setLaravelSession(Session::getFacadeRoot()); + + $data = $this->pullAttributes($request); + + $this->assertEquals([], $data); + } +} diff --git a/tests/Unit/LoginControllerTest.php b/tests/Unit/LoginControllerTest.php new file mode 100644 index 0000000..75d91a7 --- /dev/null +++ b/tests/Unit/LoginControllerTest.php @@ -0,0 +1,115 @@ +get(route('oneofftech::login.provider', ['provider' => 'gitlab'])); + + $response->assertRedirect(); + + $location = urldecode($response->headers->get('Location')); + + $this->assertStringContainsString('gitlab.com', $location); + $this->assertStringContainsString(route('oneofftech::login.callback', ['provider' => 'gitlab']), $location); + } + + public function test_user_login() + { + $this->useTestFixtures(); + + $user = $this->createUser(); + + $identity = $user->identities()->create([ + 'provider'=> 'gitlab', + 'provider_id'=> IdentityCrypt::hash('U1'), + 'token'=> 'T1', + 'refresh_token'=> null, + 'expires_at'=> null, + 'registration' => true, + ]); + + $this->withoutExceptionHandling(); + + $driverMock = Mockery::mock(Provider::class)->makePartial(); + + $oauthFakeUser = (new OauthUser())->map([ + 'id' => 'U1', + 'nickname' => 'User', + 'name' => 'User', + 'email' => 'user@local.com', + 'avatar' => 'https://gitlab.com', + 'token' => 'T1', + ]); + + $driverMock->shouldReceive('user')->andReturn($oauthFakeUser); + + $driverMock->shouldReceive('redirectUrl')->andReturn($driverMock); + + IdentityFacade::shouldReceive('driver')->with('gitlab')->andReturn($driverMock); + + $response = $this->get(route('oneofftech::login.callback', ['provider' => 'gitlab'])); + + $response->assertRedirect('http://localhost/home'); + + $this->assertAuthenticatedAs($user); + } + + public function test_user_login_denied_if_identity_cannot_be_found() + { + $this->useTestFixtures(); + + $user = $this->createUser(); + + $identity = $user->identities()->create([ + 'provider'=> 'facebook', + 'provider_id'=> IdentityCrypt::hash('U2'), + 'token'=> 'T1', + 'refresh_token'=> null, + 'expires_at'=> null, + 'registration' => true, + ]); + + $this->withoutExceptionHandling(); + + $driverMock = Mockery::mock(Provider::class)->makePartial(); + + $oauthFakeUser = (new OauthUser())->map([ + 'id' => 'U1', + 'nickname' => 'User', + 'name' => 'User', + 'email' => 'user@local.com', + 'avatar' => 'https://gitlab.com', + 'token' => 'T1', + ]); + + $driverMock->shouldReceive('user')->andReturn($oauthFakeUser); + + $driverMock->shouldReceive('redirectUrl')->andReturn($driverMock); + + IdentityFacade::shouldReceive('driver')->with('gitlab')->andReturn($driverMock); + + $this->expectException(ValidationException::class); + + $response = $this->get(route('oneofftech::login.callback', ['provider' => 'gitlab'])); + + $this->assertGuest(); + } +} diff --git a/tests/Unit/RegistrationControllerTest.php b/tests/Unit/RegistrationControllerTest.php new file mode 100644 index 0000000..c6021a9 --- /dev/null +++ b/tests/Unit/RegistrationControllerTest.php @@ -0,0 +1,104 @@ +get(route('oneofftech::register.provider', ['provider' => 'gitlab'])); + + $response->assertRedirect(); + + $location = urldecode($response->headers->get('Location')); + + $this->assertStringContainsString('gitlab.com', $location); + $this->assertStringContainsString(route('oneofftech::register.callback', ['provider' => 'gitlab']), $location); + } + + public function test_user_can_be_registered() + { + IdentityFacade::useIdentityModel('Tests\\Fixtures\\Identity'); + + $this->withoutExceptionHandling(); + + $driverMock = Mockery::mock(Provider::class)->makePartial(); + + $oauthFakeUser = (new OauthUser())->map([ + 'id' => 'U1', + 'nickname' => 'User', + 'name' => 'User', + 'email' => 'user@local.com', + 'avatar' => 'https://gitlab.com', + 'token' => 'T1', + ]); + + $driverMock->shouldReceive('user')->andReturn($oauthFakeUser); + + $driverMock->shouldReceive('redirectUrl')->andReturn($driverMock); + + IdentityFacade::shouldReceive('driver')->with('gitlab')->andReturn($driverMock); + + $response = $this->get(route('oneofftech::register.callback', ['provider' => 'gitlab'])); + + $response->assertRedirect('http://localhost/home'); + + $user = User::first(); + + $this->assertNotNull($user); + $this->assertEquals('User', $user->name); + $this->assertEquals('user@local.com', $user->email); + + $linkedIdentities = $user->identities; + + $this->assertNotNull($linkedIdentities); + $this->assertEquals(1, $linkedIdentities->count()); + + $firstIdentity = $linkedIdentities->first(); + + $this->assertEquals(IdentityCrypt::hash('U1'), $firstIdentity->provider_id); + $this->assertEquals('gitlab', $firstIdentity->provider); + $this->assertNotNull($firstIdentity->token); + } + + public function test_user_cannot_register_twice() + { + $this->createUser(); + + $this->withoutExceptionHandling(); + + $driverMock = Mockery::mock(Provider::class)->makePartial(); + + $oauthFakeUser = (new OauthUser())->map([ + 'id' => 'U1', + 'nickname' => 'User', + 'name' => 'User', + 'email' => 'user@local.com', + 'avatar' => 'https://gitlab.com', + 'token' => 'T1', + ]); + + $driverMock->shouldReceive('user')->andReturn($oauthFakeUser); + + $driverMock->shouldReceive('redirectUrl')->andReturn($driverMock); + + IdentityFacade::shouldReceive('driver')->with('gitlab')->andReturn($driverMock); + + $this->expectException(ValidationException::class); + + $this->get(route('oneofftech::register.callback', ['provider' => 'gitlab'])); + } +} diff --git a/tests/Unit/ScaffoldAuthenticationControllersTest.php b/tests/Unit/ScaffoldAuthenticationControllersTest.php new file mode 100644 index 0000000..a8ada24 --- /dev/null +++ b/tests/Unit/ScaffoldAuthenticationControllersTest.php @@ -0,0 +1,48 @@ + true]); + + $this->assertEquals(0, $exit); + + $this->assertStringContainsString('\Oneofftech\Identities\Facades\Identity::routes();', file_get_contents($webRoutesFile)); + + $files = [ + base_path('database/migrations/2020_08_09_115707_create_identities_table.php'), + app_path('Identity.php'), + app_path('Http/Controllers/Identities/Auth/ConnectController.php'), + app_path('Http/Controllers/Identities/Auth/LoginController.php'), + app_path('Http/Controllers/Identities/Auth/RegisterController.php'), + ]; + + foreach ($files as $file) { + $exists = File::exists($file); + + $this->assertTrue($exists, "Expected [$file] do not exists."); + + if ($exists) { + unlink($file); + } + } + } +}