Skip to content

Commit 515ce4e

Browse files
authored
Add unit tests to cover the main features (#4)
* Test identity traits * Test for scaffold command * Registration test * Login tests * Connect identity test
1 parent c4a1c03 commit 515ce4e

16 files changed

+851
-10
lines changed

src/Facades/Identity.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,8 @@ public static function events()
9393
*/
9494
public static function findUserByIdOrFail(string $id)
9595
{
96-
return static::newUserModel()->where('id', $id)->firstOrFail();
96+
$model = static::newUserModel();
97+
return $model->where($model->getKeyName(), $id)->firstOrFail();
9798
}
9899

99100
/**

src/Providers/IdentitiesServiceProvider.php

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,9 @@ public function register()
3838
$config = $app->make('config')->get('identities');
3939
$app_config = $app->make('config')->get('app');
4040

41-
if (Str::startsWith($key = $config['key'], 'base64:')) {
41+
$key = $config['key'] ?? $app_config['key'];
42+
43+
if (Str::startsWith($key, 'base64:')) {
4244
$key = base64_decode(substr($key, 7));
4345
}
4446

src/Support/FindIdentity.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,10 @@
99
trait FindIdentity
1010
{
1111
/**
12+
* Search a user given its identity in the third party provider
13+
*
14+
* @param \Laravel\Socialite\Contracts\User $identity Third party identity provided by Laravel Socialite
15+
* @param string $provider The identity provider name
1216
* @return \Illuminate\Contracts\Auth\Authenticatable|null
1317
*/
1418
protected function findUserFromIdentity($identity, $provider)
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
<?php
2+
3+
namespace Tests\Fixtures\Concern;
4+
5+
use Oneofftech\Identities\Facades\Identity as IdentityFacade;
6+
7+
trait UseTestFixtures
8+
{
9+
protected function useTestFixtures()
10+
{
11+
IdentityFacade::useUserModel("Tests\\Fixtures\\User");
12+
IdentityFacade::useIdentityModel('Tests\\Fixtures\\Identity');
13+
IdentityFacade::useNamespace('Tests\\Fixtures');
14+
IdentityFacade::routes();
15+
}
16+
}

tests/Fixtures/User.php

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,4 +8,10 @@
88
class User extends Authenticatable
99
{
1010
use WithIdentities;
11+
12+
protected $fillable = [
13+
'name',
14+
'email',
15+
'password',
16+
];
1117
}

tests/TestCase.php

Lines changed: 51 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,13 @@
77
use Illuminate\Events\Dispatcher;
88
use Laravel\Socialite\SocialiteServiceProvider;
99
use Orchestra\Testbench\TestCase as BaseTestCase;
10+
use Oneofftech\Identities\Facades\Identity as IdentityFacade;
1011
use Oneofftech\Identities\Providers\IdentitiesServiceProvider;
1112
use SocialiteProviders\Dropbox\DropboxExtendSocialite;
1213
use SocialiteProviders\GitLab\GitLabExtendSocialite;
1314
use SocialiteProviders\Manager\SocialiteWasCalled;
15+
use Tests\Fixtures\Concern\UseTestFixtures;
16+
use Tests\Fixtures\User;
1417

1518
abstract class TestCase extends BaseTestCase
1619
{
@@ -22,9 +25,26 @@ public function setUp(): void
2225
{
2326
parent::setUp();
2427

28+
$this->setUpDatabase();
29+
30+
$this->setUpSession($this->app);
31+
2532
$this->activateSocialiteExtensions();
2633
}
2734

35+
protected function setUpTraits()
36+
{
37+
parent::setUpTraits();
38+
39+
$uses = \array_flip(\class_uses_recursive(static::class));
40+
41+
if (isset($uses[UseTestFixtures::class])) {
42+
$this->useTestFixtures();
43+
}
44+
45+
return $uses;
46+
}
47+
2848
/**
2949
* Define environment setup.
3050
*
@@ -55,9 +75,9 @@ protected function getEnvironmentSetUp($app)
5575
]);
5676

5777
$key = Str::random(32);
58-
$app['config']->set('app.key', $key);
78+
$app['config']->set('app.key', 'base64:'.base64_encode($key));
5979
$app['config']->set('app.cipher', 'AES-256-CBC');
60-
$app['config']->set('identities.key', $key);
80+
$app['config']->set('identities.key', 'base64:'.base64_encode($key));
6181
}
6282

6383
/**
@@ -72,6 +92,23 @@ protected function getPackageProviders($app)
7292
];
7393
}
7494

95+
protected function setUpDatabase()
96+
{
97+
$this->loadLaravelMigrations();
98+
99+
$this->loadMigrationsFrom(__DIR__.'/../stubs/migrations');
100+
101+
IdentityFacade::useNamespace("App");
102+
IdentityFacade::useIdentityModel("App\\Identity");
103+
IdentityFacade::useUserModel("App\\User");
104+
}
105+
106+
protected function setUpSession()
107+
{
108+
$kernel = $this->app->make('Illuminate\Contracts\Http\Kernel');
109+
$kernel->pushMiddleware('Illuminate\Session\Middleware\StartSession');
110+
}
111+
75112
protected function activateSocialiteExtensions()
76113
{
77114
$socialiteWasCalled = $this->app->make(SocialiteWasCalled::class);
@@ -97,4 +134,16 @@ public function assertListenerIsAttachedToEvent($listener, $event)
97134

98135
$this->assertTrue(false, sprintf('Event %s does not have the %s listener attached to it', $event, $listener));
99136
}
137+
138+
public function createUser($data = [])
139+
{
140+
return tap((new User), function ($u) use ($data) {
141+
$u->forceFill(array_merge([
142+
'email' => '[email protected]',
143+
'name' => 'User',
144+
'password' => '$2y$10$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi',
145+
'remember_token' => Str::random(10),
146+
], $data))->save();
147+
});
148+
}
100149
}

tests/Unit/ConnectControllerTest.php

Lines changed: 159 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,159 @@
1+
<?php
2+
3+
namespace Tests\Unit;
4+
5+
use Mockery;
6+
use Carbon\Carbon;
7+
use Tests\TestCase;
8+
use Tests\Fixtures\User;
9+
use Illuminate\Support\Str;
10+
use SocialiteProviders\GitLab\Provider;
11+
use Tests\Fixtures\Concern\UseTestFixtures;
12+
use Illuminate\Auth\AuthenticationException;
13+
use Oneofftech\Identities\Facades\IdentityCrypt;
14+
use Illuminate\Foundation\Testing\RefreshDatabase;
15+
use SocialiteProviders\Manager\OAuth2\User as OauthUser;
16+
use Oneofftech\Identities\Facades\Identity as IdentityFacade;
17+
18+
class ConnectControllerTest extends TestCase
19+
{
20+
use RefreshDatabase, UseTestFixtures;
21+
22+
public function test_redirect_to_provider_require_authentication()
23+
{
24+
IdentityFacade::useNamespace('Tests\\Fixtures');
25+
IdentityFacade::routes();
26+
27+
$router = $this->app->make('router');
28+
29+
$router->get('login', function () {
30+
})->name('login');
31+
32+
$this->withoutExceptionHandling();
33+
34+
$this->expectException(AuthenticationException::class);
35+
$this->expectExceptionMessage('Unauthenticated');
36+
37+
$this->get(route('oneofftech::connect.provider', ['provider' => 'gitlab']));
38+
}
39+
40+
public function test_redirect_to_provider()
41+
{
42+
IdentityFacade::useNamespace('Tests\\Fixtures');
43+
IdentityFacade::routes();
44+
45+
$user = tap((new User()), function ($u) {
46+
$u->forceFill([
47+
'email' => '[email protected]',
48+
'name' => 'User',
49+
'password' => '$2y$10$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi',
50+
'remember_token' => Str::random(10),
51+
])->save();
52+
});
53+
54+
$response = $this->actingAs($user)
55+
->get(route('oneofftech::connect.provider', ['provider' => 'gitlab']));
56+
57+
$response->assertRedirect();
58+
59+
$location = urldecode($response->headers->get('Location'));
60+
61+
$this->assertStringContainsString('gitlab.com', $location);
62+
$this->assertStringContainsString(route('oneofftech::connect.callback', ['provider' => 'gitlab']), $location);
63+
}
64+
65+
public function test_connect_creates_identity()
66+
{
67+
$this->useTestFixtures();
68+
69+
$user = $this->createUser();
70+
71+
$this->withoutExceptionHandling();
72+
73+
$driverMock = Mockery::mock(Provider::class)->makePartial();
74+
75+
Carbon::setTestNow(Carbon::create(2020, 11, 12, 10, 20));
76+
77+
$oauthFakeUser = (new OauthUser())->map([
78+
'id' => 'U1',
79+
'nickname' => 'User',
80+
'name' => 'User',
81+
'email' => '[email protected]',
82+
'avatar' => 'https://gitlab.com',
83+
'token' => 'T2',
84+
'refreshToken' => 'RT2',
85+
'expiresIn' => Carbon::SECONDS_PER_MINUTE,
86+
]);
87+
88+
$driverMock->shouldReceive('user')->andReturn($oauthFakeUser);
89+
90+
$driverMock->shouldReceive('redirectUrl')->andReturn($driverMock);
91+
92+
IdentityFacade::shouldReceive('driver')->with('gitlab')->andReturn($driverMock);
93+
94+
$response = $this->actingAs($user)
95+
->get(route('oneofftech::connect.callback', ['provider' => 'gitlab']));
96+
97+
$response->assertRedirect('http://localhost/home');
98+
99+
$updatedIdentity = $user->identities->first();
100+
101+
$this->assertEquals(IdentityCrypt::hash('U1'), $updatedIdentity->provider_id);
102+
$this->assertEquals('gitlab', $updatedIdentity->provider);
103+
$this->assertEquals(Carbon::create(2020, 11, 12, 10, 21), $updatedIdentity->expires_at);
104+
$this->assertEquals('T2', IdentityCrypt::decryptString($updatedIdentity->token));
105+
$this->assertEquals('RT2', IdentityCrypt::decryptString($updatedIdentity->refresh_token));
106+
}
107+
108+
public function test_connect_updates_existing_identity()
109+
{
110+
$this->useTestFixtures();
111+
112+
$user = $this->createUser();
113+
114+
$identity = $user->identities()->create([
115+
'provider'=> 'gitlab',
116+
'provider_id'=> IdentityCrypt::hash('U1'),
117+
'token'=> 'T1',
118+
'refresh_token'=> null,
119+
'expires_at'=> null,
120+
'registration' => true,
121+
]);
122+
123+
$this->withoutExceptionHandling();
124+
125+
$driverMock = Mockery::mock(Provider::class)->makePartial();
126+
127+
Carbon::setTestNow(Carbon::create(2020, 11, 12, 10, 20));
128+
129+
$oauthFakeUser = (new OauthUser())->map([
130+
'id' => 'U1',
131+
'nickname' => 'User',
132+
'name' => 'User',
133+
'email' => '[email protected]',
134+
'avatar' => 'https://gitlab.com',
135+
'token' => 'T2',
136+
'refreshToken' => 'RT2',
137+
'expiresIn' => Carbon::SECONDS_PER_MINUTE,
138+
]);
139+
140+
$driverMock->shouldReceive('user')->andReturn($oauthFakeUser);
141+
142+
$driverMock->shouldReceive('redirectUrl')->andReturn($driverMock);
143+
144+
IdentityFacade::shouldReceive('driver')->with('gitlab')->andReturn($driverMock);
145+
146+
$response = $this->actingAs($user)
147+
->get(route('oneofftech::connect.callback', ['provider' => 'gitlab']));
148+
149+
$response->assertRedirect('http://localhost/home');
150+
151+
$updatedIdentity = $user->identities->first();
152+
153+
$this->assertEquals($identity->provider_id, $updatedIdentity->provider_id);
154+
$this->assertEquals('gitlab', $updatedIdentity->provider);
155+
$this->assertEquals(Carbon::create(2020, 11, 12, 10, 21), $updatedIdentity->expires_at);
156+
$this->assertEquals('T2', IdentityCrypt::decryptString($updatedIdentity->token));
157+
$this->assertEquals('RT2', IdentityCrypt::decryptString($updatedIdentity->refresh_token));
158+
}
159+
}

tests/Unit/EncrypterTest.php

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,9 @@ public function test_value_encrypted_with_old_key_can_be_decrypted()
2020
{
2121
$encrypter = new Encrypter(config('identities.old_key'), 'AES-256-CBC');
2222

23-
$encryptedWithOldKey = $encrypter->encrypt('test');
23+
$encryptedWithOldKey = $encrypter->encryptString('test');
2424

25-
$decrypted = IdentityCrypt::decrypt($encryptedWithOldKey);
25+
$decrypted = IdentityCrypt::decryptString($encryptedWithOldKey);
2626

2727
$this->assertEquals('test', $decrypted);
2828
}
@@ -33,18 +33,18 @@ public function test_value_encrypted_with_old_base64_key_can_be_decrypted()
3333
$this->app['config']->set('identities.old_key', 'base64:'.base64_encode($key));
3434
$encrypter = new Encrypter($key, 'AES-256-CBC');
3535

36-
$encryptedWithOldKey = $encrypter->encrypt('test');
36+
$encryptedWithOldKey = $encrypter->encryptString('test');
3737

38-
$decrypted = IdentityCrypt::decrypt($encryptedWithOldKey);
38+
$decrypted = IdentityCrypt::decryptString($encryptedWithOldKey);
3939

4040
$this->assertEquals('test', $decrypted);
4141
}
4242

4343
public function test_value_can_be_encrypted()
4444
{
45-
$encrypted = IdentityCrypt::encrypt('test');
45+
$encrypted = IdentityCrypt::encryptString('test');
4646

47-
$decrypted = IdentityCrypt::decrypt($encrypted);
47+
$decrypted = IdentityCrypt::decryptString($encrypted);
4848

4949
$this->assertEquals('test', $decrypted);
5050
}

tests/Unit/IdentityServiceProviderTest.php

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,22 @@
1111
use Illuminate\Foundation\Testing\DatabaseMigrations;
1212
use InvalidArgumentException;
1313
use Laravel\Socialite\Two\FacebookProvider;
14+
use Oneofftech\Identities\Providers\IdentitiesServiceProvider;
1415
use SocialiteProviders\GitLab\Provider as GitlabSocialiteProvider;
1516

1617
class IdentityServiceProviderTest extends TestCase
1718
{
1819
use DatabaseMigrations;
1920

21+
public function test_default_driver_cannot_not_configured()
22+
{
23+
$factory = $this->app->make(IdentitiesManager::class);
24+
25+
$this->expectException(InvalidArgumentException::class);
26+
27+
$factory->redirect();
28+
}
29+
2030
public function test_it_can_instantiate_the_gitlab_driver()
2131
{
2232
$factory = $this->app->make(IdentitiesManager::class);
@@ -101,4 +111,11 @@ public function test_facade_return_manager_instance()
101111
{
102112
$this->assertInstanceOf(IdentitiesManager::class, Identity::getFacadeRoot());
103113
}
114+
115+
public function test_provider_lists_provided_services()
116+
{
117+
$provides = (new IdentitiesServiceProvider($this->app))->provides();
118+
119+
$this->assertEquals([IdentitiesManager::class], $provides);
120+
}
104121
}

0 commit comments

Comments
 (0)