Prechádzať zdrojové kódy

Add tests for the Authentication log feature

Bubka 1 rok pred
rodič
commit
5a74a37348

+ 277 - 0
tests/Api/v1/Controllers/UserManagerControllerTest.php

@@ -10,6 +10,7 @@ use Database\Factories\UserFactory;
 use Illuminate\Auth\Notifications\ResetPassword;
 use Illuminate\Http\Request;
 use Illuminate\Support\Arr;
+use Illuminate\Support\Carbon;
 use Illuminate\Support\Facades\DB;
 use Illuminate\Support\Facades\Hash;
 use Illuminate\Support\Facades\Notification;
@@ -19,6 +20,8 @@ use Illuminate\Support\Str;
 use Laravel\Passport\TokenRepository;
 use Mockery\MockInterface;
 use PHPUnit\Framework\Attributes\CoversClass;
+use PHPUnit\Framework\Attributes\DataProvider;
+use Tests\Data\AuthenticationLogData;
 use Tests\FeatureTestCase;
 
 #[CoversClass(UserManagerController::class)]
@@ -518,4 +521,278 @@ class UserManagerControllerTest extends FeatureTestCase
             ])
             ->assertForbidden();
     }
+
+    protected function feedAuthenticationLog() : int
+    {
+        // Do not change creation order
+        $this->user->authentications()->create(AuthenticationLogData::beforeLastYear());
+        $this->user->authentications()->create(AuthenticationLogData::duringLastYear());
+        $this->user->authentications()->create(AuthenticationLogData::duringLastSixMonth());
+        $this->user->authentications()->create(AuthenticationLogData::duringLastThreeMonth());
+        $this->user->authentications()->create(AuthenticationLogData::duringLastMonth());
+        $this->user->authentications()->create(AuthenticationLogData::noLogin());
+        $this->user->authentications()->create(AuthenticationLogData::noLogout());
+
+        return 7;
+    }
+
+    /**
+     * @test
+     */
+    public function test_authentications_returns_all_entries() : void
+    {
+        $created = $this->feedAuthenticationLog();
+        
+        $this->actingAs($this->admin, 'api-guard')
+            ->json('GET', '/api/v1/users/' . $this->user->id . '/authentications')
+            ->assertOk()
+            ->assertJsonCount($created);
+    }
+
+    /**
+     * @test
+     */
+    public function test_authentications_returns_expected_resource() : void
+    {
+        $this->user->authentications()->create(AuthenticationLogData::duringLastMonth());
+        
+        $this->actingAs($this->admin, 'api-guard')
+            ->json('GET', '/api/v1/users/' . $this->user->id . '/authentications')
+            ->assertJsonStructure([
+                '*' => [
+                    'id',
+                    'ip_address',
+                    'user_agent',
+                    'browser',
+                    'platform',
+                    'device',
+                    'login_at',
+                    'logout_at',
+                    'login_successful',
+                    'duration',
+                ]
+            ]);
+    }
+
+    /**
+     * @test
+     */
+    public function test_authentications_returns_no_login_entry() : void
+    {
+        $this->user->authentications()->create(AuthenticationLogData::noLogin());
+        
+        $this->actingAs($this->admin, 'api-guard')
+            ->json('GET', '/api/v1/users/' . $this->user->id . '/authentications?period=1')
+            ->assertJsonCount(1)
+            ->assertJsonFragment([
+                'login_at' => null
+            ]);
+    }
+
+    /**
+     * @test
+     */
+    public function test_authentications_returns_no_logout_entry() : void
+    {
+        $this->user->authentications()->create(AuthenticationLogData::noLogout());
+        
+        $this->actingAs($this->admin, 'api-guard')
+            ->json('GET', '/api/v1/users/' . $this->user->id . '/authentications?period=1')
+            ->assertJsonCount(1)
+            ->assertJsonFragment([
+                'logout_at' => null
+            ]);
+    }
+
+    /**
+     * @test
+     */
+    public function test_authentications_returns_failed_entry() : void
+    {
+        $this->user->authentications()->create(AuthenticationLogData::failedLogin());
+        $expected = Carbon::parse(AuthenticationLogData::failedLogin()['login_at'])->toDayDateTimeString();
+        
+        $this->actingAs($this->admin, 'api-guard')
+            ->json('GET', '/api/v1/users/' . $this->user->id . '/authentications?period=1')
+            ->assertJsonCount(1)
+            ->assertJsonFragment([
+                'login_at' => $expected,
+                'login_successful' => false,
+            ]);
+    }
+
+    /**
+     * @test
+     */
+    public function test_authentications_returns_last_month_entries() : void
+    {
+        $this->feedAuthenticationLog();
+        $expected = Carbon::parse(AuthenticationLogData::duringLastMonth()['login_at'])->toDayDateTimeString();
+        
+        $this->actingAs($this->admin, 'api-guard')
+            ->json('GET', '/api/v1/users/' . $this->user->id . '/authentications?period=1')
+            ->assertJsonCount(3)
+            ->assertJsonFragment([
+                'login_at' => $expected
+            ]);
+    }
+
+    /**
+     * @test
+     */
+    public function test_authentications_returns_last_three_months_entries() : void
+    {
+        $this->feedAuthenticationLog();
+        $expectedOneMonth = Carbon::parse(AuthenticationLogData::duringLastMonth()['login_at'])->toDayDateTimeString();
+        $expectedThreeMonth = Carbon::parse(AuthenticationLogData::duringLastThreeMonth()['login_at'])->toDayDateTimeString();
+        
+        $this->actingAs($this->admin, 'api-guard')
+            ->json('GET', '/api/v1/users/' . $this->user->id . '/authentications?period=3')
+            ->assertJsonCount(4)
+            ->assertJsonFragment([
+                'login_at' => $expectedOneMonth
+            ])
+            ->assertJsonFragment([
+                'login_at' => $expectedThreeMonth
+            ]);
+    }
+
+    /**
+     * @test
+     */
+    public function test_authentications_returns_last_six_months_entries() : void
+    {
+        $this->feedAuthenticationLog();
+        $expectedOneMonth = Carbon::parse(AuthenticationLogData::duringLastMonth()['login_at'])->toDayDateTimeString();
+        $expectedThreeMonth = Carbon::parse(AuthenticationLogData::duringLastThreeMonth()['login_at'])->toDayDateTimeString();
+        $expectedSixMonth = Carbon::parse(AuthenticationLogData::duringLastSixMonth()['login_at'])->toDayDateTimeString();
+        
+        $this->actingAs($this->admin, 'api-guard')
+            ->json('GET', '/api/v1/users/' . $this->user->id . '/authentications?period=6')
+            ->assertJsonCount(5)
+            ->assertJsonFragment([
+                'login_at' => $expectedOneMonth
+            ])
+            ->assertJsonFragment([
+                'login_at' => $expectedThreeMonth
+            ])
+            ->assertJsonFragment([
+                'login_at' => $expectedSixMonth
+            ]);
+    }
+
+    /**
+     * @test
+     */
+    public function test_authentications_returns_last_year_entries() : void
+    {
+        $this->feedAuthenticationLog();
+        $expectedOneMonth = Carbon::parse(AuthenticationLogData::duringLastMonth()['login_at'])->toDayDateTimeString();
+        $expectedThreeMonth = Carbon::parse(AuthenticationLogData::duringLastThreeMonth()['login_at'])->toDayDateTimeString();
+        $expectedSixMonth = Carbon::parse(AuthenticationLogData::duringLastSixMonth()['login_at'])->toDayDateTimeString();
+        $expectedYear = Carbon::parse(AuthenticationLogData::duringLastYear()['login_at'])->toDayDateTimeString();
+        
+        $this->actingAs($this->admin, 'api-guard')
+            ->json('GET', '/api/v1/users/' . $this->user->id . '/authentications?period=12')
+            ->assertJsonCount(6)
+            ->assertJsonFragment([
+                'login_at' => $expectedOneMonth
+            ])
+            ->assertJsonFragment([
+                'login_at' => $expectedThreeMonth
+            ])
+            ->assertJsonFragment([
+                'login_at' => $expectedSixMonth
+            ])
+            ->assertJsonFragment([
+                'login_at' => $expectedYear
+            ]);
+    }
+
+    /**
+     * @test
+     */
+    #[DataProvider('LimitProvider')]
+    public function test_authentications_returns_limited_entries($limit) : void
+    {
+        $this->feedAuthenticationLog();
+        
+        $this->actingAs($this->admin, 'api-guard')
+            ->json('GET', '/api/v1/users/' . $this->user->id . '/authentications?limit=' . $limit)
+            ->assertOk()
+            ->assertJsonCount($limit);
+    }
+
+    /**
+     * Provide various limit
+     */
+    public static function LimitProvider()
+    {
+        return [
+            'limited to 1' => [1],
+            'limited to 2' => [2],
+            'limited to 3' => [3],
+        ];
+    }
+
+    /**
+     * @test
+     */
+    public function test_authentications_returns_expected_ip_and_useragent_chunks() : void
+    {
+        $this->user->authentications()->create([
+            'ip_address' => '127.0.0.1',
+            'user_agent' => 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) Gecko/20100101 Firefox/115.0',
+            'login_at' => now(),
+            'login_successful' => true,
+            'logout_at' => null,
+            'location' => null,
+        ]);
+        
+        $this->actingAs($this->admin, 'api-guard')
+            ->json('GET', '/api/v1/users/' . $this->user->id . '/authentications?period=1')
+            ->assertJsonFragment([
+                'ip_address' => '127.0.0.1',
+                'browser' => 'Firefox',
+                'platform' => 'Windows',
+                'device' => 'desktop',
+            ]);
+    }
+
+    /**
+     * @test
+     */
+    #[DataProvider('invalidQueryParameterProvider')]
+    public function test_authentications_with_invalid_limit_returns_validation_error($limit) : void
+    {
+        $this->actingAs($this->admin, 'api-guard')
+            ->json('GET', '/api/v1/users/' . $this->user->id . '/authentications?limit=' . $limit)
+            ->assertStatus(422);
+    }
+
+    /**
+     * @test
+     */
+    #[DataProvider('invalidQueryParameterProvider')]
+    public function test_authentications_with_invalid_period_returns_validation_error($period) : void
+    {
+        $this->actingAs($this->admin, 'api-guard')
+            ->json('GET', '/api/v1/users/' . $this->user->id . '/authentications?period=' . $period)
+            ->assertStatus(422);
+    }
+
+    /**
+     * Provide various invalid value to test query parameter
+     */
+    public static function invalidQueryParameterProvider()
+    {
+        return [
+            'empty' => [''],
+            'null' => ['null'],
+            'boolean' => ['true'],
+            'string' => ['string'],
+            'array' => ['[]'],
+        ];
+    }
+    
 }

+ 159 - 0
tests/Data/AuthenticationLogData.php

@@ -0,0 +1,159 @@
+<?php
+
+namespace Tests\Data;
+
+class AuthenticationLogData
+{
+    /**
+     * Indicate that the model should have login date.
+     *
+     * @return array
+     */
+    public static function failedLogin()
+    {
+        $loginDate = now()->subDays(15);
+    
+        return [
+            'ip_address' => fake()->ipv4(),
+            'user_agent' => fake()->userAgent(),
+            'login_at' => $loginDate,
+            'login_successful' => false,
+            'logout_at' => null,
+            'location' => null,
+        ];
+    }
+
+    /**
+     * Indicate that the model should have no login date
+     *
+     * @return array
+     */
+    public static function noLogin()
+    {
+        return [
+            'ip_address' => fake()->ipv4(),
+            'user_agent' => fake()->userAgent(),
+            'login_at' => null,
+            'login_successful' => false,
+            'logout_at' => now(),
+            'location' => null,
+        ];
+    }
+
+    /**
+     * Indicate that the model should have no logout date
+     *
+     * @return array
+     */
+    public static function noLogout()
+    {
+        return [
+            'ip_address' => fake()->ipv4(),
+            'user_agent' => fake()->userAgent(),
+            'login_at' => now(),
+            'login_successful' => true,
+            'logout_at' => null,
+            'location' => null,
+        ];
+    }
+
+    /**
+     * Indicate that the model should have login during last month
+     *
+     * @return array
+     */
+    public static function duringLastMonth()
+    {
+        $loginDate = now()->subDays(15);
+        $logoutDate = $loginDate->addHours(1);
+    
+        return [
+            'ip_address' => '127.0.0.1',
+            'user_agent' => 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) Gecko/20100101 Firefox/115.0',
+            'login_at' => $loginDate,
+            'login_successful' => true,
+            'logout_at' => $logoutDate,
+            'location' => null,
+        ];
+    }
+
+    /**
+     * Indicate that the model should have login during last 3 month
+     *
+     * @return array
+     */
+    public static function duringLastThreeMonth()
+    {
+        $loginDate = now()->subMonths(2);
+        $logoutDate = $loginDate->addHours(1);
+    
+        return [
+            'ip_address' => fake()->ipv4(),
+            'user_agent' => fake()->userAgent(),
+            'login_at' => $loginDate,
+            'login_successful' => true,
+            'logout_at' => $logoutDate,
+            'location' => null,
+        ];
+    }
+
+    /**
+     * Indicate that the model should have login during last 6 month
+     *
+     * @return array
+     */
+    public static function duringLastSixMonth()
+    {
+        $loginDate = now()->subMonths(4);
+        $logoutDate = $loginDate->addHours(1);
+    
+        return [
+            'ip_address' => fake()->ipv4(),
+            'user_agent' => fake()->userAgent(),
+            'login_at' => $loginDate,
+            'login_successful' => true,
+            'logout_at' => $logoutDate,
+            'location' => null,
+        ];
+    }
+
+    /**
+     * Indicate that the model should have login during last month
+     *
+     * @return array
+     */
+    public static function duringLastYear()
+    {
+        $loginDate = now()->subMonths(10);
+        $logoutDate = $loginDate->addHours(1);
+    
+        return [
+            'ip_address' => fake()->ipv4(),
+            'user_agent' => fake()->userAgent(),
+            'login_at' => $loginDate,
+            'login_successful' => true,
+            'logout_at' => $logoutDate,
+            'location' => null,
+        ];
+    }
+
+    /**
+     * Indicate that the model should have login during last month
+     *
+     * @return array
+     */
+    public static function beforeLastYear()
+    {
+        $loginDate = now()->subYears(2);
+        $logoutDate = $loginDate->addHours(1);
+    
+        return [
+            'ip_address' => fake()->ipv4(),
+            'user_agent' => fake()->userAgent(),
+            'login_at' => $loginDate,
+            'login_successful' => true,
+            'logout_at' => $logoutDate,
+            'location' => null,
+        ];
+    }
+}