5 Commits 7e9a2f314e ... e433262c4c

Author SHA1 Message Date
  Felix Freeman e433262c4c Complete AuthController tests 4 years ago
  Felix Freeman 25244507c2 AuthController::emailToken() require username 4 years ago
  Felix Freeman 01850ce3cb User::login() and ::amI() functions 4 years ago
  Felix Freeman 4ee3c92040 Complete Paginator tests 4 years ago
  Felix Freeman 55d8599774 Paginator::links() 'prev' and 'next' set to null when I'm in first and last page respectively 4 years ago

+ 1 - 0
src/Http/Controllers/AuthController.php

@@ -59,6 +59,7 @@ class AuthController extends Controller
             );
         }
 
+        $this->validate($request, ['username' => 'required']);
         return User::emailToken($request->input('username'), $origin_url);
     }
 }

+ 18 - 5
src/Http/Controllers/Paginator.php

@@ -41,18 +41,18 @@ class Paginator
         return $this->db_query->count();
     }
 
-    private function prevPage()
+    private function prevPage(): ?int
     {
         if ($this->current_page === 1) {
-            return 1;
+            return null;
         }
         return $this->current_page - 1;
     }
 
-    private function nextPage()
+    private function nextPage(): ?int
     {
         if ($this->current_page === $this->totalPages()) {
-            return $this->totalPages();
+            return null;
         }
         return $this->current_page + 1;
     }
@@ -67,6 +67,12 @@ class Paginator
         return $this->pagedDbQuery()->get();
     }
 
+    /**
+     * @param $url string request URL
+     * @param $query_params array associative array of query params to include
+     * on links
+     * @return associative array including first, last, prev and next items
+     */
     public function links(string $url, array $query_params = []): array
     {
         $page_numbers = [
@@ -77,14 +83,21 @@ class Paginator
         ];
 
         $new_query_params = array_map(function ($value) use ($query_params) {
+            if ($value === null) {
+                return null;
+            }
+
             return array_merge(
                 $query_params,
                 ['page' => $value, 'limit' => $this->items_limit]
             );
         }, $page_numbers);
 
-        // TODO: Prev and next should be null if I'm in first and last page respectively
         return array_map(function ($value) use ($url) {
+            if ($value === null) {
+                return null;
+            }
+
             return "$url?" . http_build_query($value);
         }, $new_query_params);
     }

+ 6 - 0
src/Providers/CoreServiceProvider.php

@@ -85,5 +85,11 @@ class CoreServiceProvider extends ServiceProvider
         // https://laravel.com/docs/5.8/migrations#creating-indexes
         // Subtitle `Index Lengths & MySQL / MariaDB`
         app('db')->connection()->getSchemaBuilder()->defaultStringLength(191);
+
+        // Response macro used on tests, mimics \Illuminate\Http\JsonResponse
+        $this->app['Illuminate\Http\Response']
+             ->macro('getData', function () {
+                 return json_decode($this->getContent(), false, 512);
+             });
     }
 }

+ 16 - 3
src/User.php

@@ -45,6 +45,12 @@ class User extends TableModel
         return $this;
     }
 
+    public function login(): void
+    {
+        app('session')->set('user_uid', $this->uid);
+        app('session')->migrate();
+    }
+
     /**
      * @param string $username can be the uid or email
      * @param string $password plain text password
@@ -65,8 +71,7 @@ class User extends TableModel
 
         self::clearFailedLogins($username);
 
-        app('session')->set('user_uid', $user->uid);
-        app('session')->migrate();
+        $user->login();
 
         if ($remember) {
             $user->generateHumanToken(true);
@@ -101,7 +106,7 @@ class User extends TableModel
         $user = self::find($token->user_uid);
 
         if ($token->type == Token::HUMAN) {
-            app('session')->set('user_uid', $user->uid);
+            $user->login();
             $token->delete();
             setcookie('auth_token', '', time() - 3600, '/', null);
             if ($remember) {
@@ -251,6 +256,14 @@ class User extends TableModel
         return $mail;
     }
 
+    /**
+     * Whether am I logged in as this user or not
+     */
+    public function amI(): bool
+    {
+        return $this->uid === app('session')->get('user_uid');
+    }
+
     // Users own themselves
     public function isOwner(self $user): bool
     {

+ 183 - 0
tests/AuthControllerTest.php

@@ -0,0 +1,183 @@
+<?php
+
+// Copyright 2019 Hackware SpA <human@hackware.cl>
+// "Hackware Web Services Core" is released under the MIT License terms.
+
+namespace Hawese\Tests;
+
+// use Hawese\Core\Http\Controllers\AuthController; // ??
+use Hawese\Core\User;
+use Laravel\Lumen\Testing\DatabaseTransactions;
+
+class AuthControllerTest extends TestCase
+{
+    use DatabaseTransactions;
+
+    public function setUp(): void
+    {
+        parent::setUp();
+
+        $_SERVER['REMOTE_ADDR'] = '127.0.0.1';
+
+        $this->user = new User([
+            'uid' => 'username',
+            'email' => 'mail@doma.in'
+        ]);
+        $this->user->changePassword('password');
+        $this->user->insert();
+    }
+
+    public function testLoginWithUid()
+    {
+        $this->assertFalse($this->user->amI());
+
+        $response = $this->request(
+            'POST',
+            '/auth/login',
+            ['username' => $this->user->uid, 'password' => 'password']
+        );
+
+        $this->assertSame(
+            $this->user->uid,
+            $response->getData()->uid
+        );
+        $this->assertTrue($this->user->amI());
+    }
+
+    public function testLoginWithEmail()
+    {
+        $this->assertFalse($this->user->amI());
+
+        $response = $this->request(
+            'POST',
+            '/auth/login',
+            ['username' => $this->user->email, 'password' => 'password']
+        );
+
+        $this->assertSame(
+            $this->user->uid,
+            $response->getData()->uid
+        );
+        $this->assertTrue($this->user->amI());
+    }
+
+    public function testLoginWrongUsername()
+    {
+        $response = $this->request(
+            'POST',
+            '/auth/login',
+            ['username' => 'anything', 'password' => 'password']
+        );
+        $this->assertStringContainsString(
+            'could not be found',
+            $response->getData()->error->message
+        );
+    }
+
+    public function testLoginWrongPassword()
+    {
+        $response = $this->request(
+            'POST',
+            '/auth/login',
+            ['username' => $this->user->email, 'password' => 'not_password']
+        );
+        $this->assertStringContainsString(
+            'Wrong',
+            $response->getData()->error->message
+        );
+    }
+
+    public function testLoginNoInput()
+    {
+        $response = $this->request(
+            'POST',
+            '/auth/login',
+        );
+        $this->assertStringContainsString(
+            'invalid',
+            $response->getData()->error->message
+        );
+    }
+
+    private function validOrigin()
+    {
+        return explode(',', env('CORS_ALLOW_ORIGINS'))[0];
+    }
+
+    public function testEmailTokenWithUid()
+    {
+        $this->validOrigin();
+        $response = $this->request(
+            'POST',
+            '/auth/email-token',
+            ['username' => $this->user->uid],
+            ['Referer' => $this->validOrigin()]
+        );
+        $this->assertSame(
+            'm**l@doma.in',
+            $response->getData()->To[0][0]
+        );
+    }
+
+    public function testEmailTokenWithEmail()
+    {
+        $this->validOrigin();
+        $response = $this->request(
+            'POST',
+            "/auth/email-token?origin_url={$this->validOrigin()}",
+            ['username' => $this->user->email]
+        );
+        $this->assertSame(
+            'm**l@doma.in',
+            $response->getData()->To[0][0]
+        );
+    }
+
+    public function testEmailTokenWrongUsername()
+    {
+        $response = $this->request(
+            'POST',
+            '/auth/email-token',
+            ['username' => 'not_username'],
+            ['Referer' => $this->validOrigin()]
+        );
+        $this->assertStringContainsString(
+            'could not be found',
+            $response->getData()->error->message
+        );
+    }
+
+    public function testEmailTokenNoInput()
+    {
+        $response = $this->request(
+            'POST',
+            '/auth/email-token',
+            [],
+            ['Referer' => $this->validOrigin()]
+        );
+        $this->assertStringContainsString(
+            'invalid',
+            $response->getData()->error->message
+        );
+    }
+
+    public function testWhoAmI()
+    {
+        $this->user->login();
+        $this->assertSame(
+            $this->user->uid,
+            $this->request('GET', '/auth/whoami')->getData()->uid
+        );
+    }
+
+    public function testLogout()
+    {
+        $this->user->login();
+        $this->assertTrue($this->user->amI());
+        $this->assertSame(
+            'true',
+            $this->request('POST', '/auth/logout')->getContent()
+        );
+        $this->assertFalse($this->user->amI());
+    }
+}

+ 71 - 0
tests/PaginatorTest.php

@@ -0,0 +1,71 @@
+<?php
+
+// Copyright 2019 Hackware SpA <human@hackware.cl>
+// "Hackware Web Services Core" is released under the MIT License terms.
+
+namespace Hawese\Tests;
+
+use Hawese\Core\Http\Controllers\Paginator;
+use Laravel\Lumen\Testing\DatabaseTransactions;
+
+class PaginatorTest extends TestCase
+{
+    use DatabaseTransactions;
+
+    private const LIMIT = 10;
+
+    public function setUp(): void
+    {
+        parent::setUp();
+
+        for ($i = 0; $i < 21; $i++) {
+            (new DumbTableModel([
+                'attr1' => bin2hex(random_bytes(12))
+            ]))->insert();
+        }
+
+        $this->paginator = new Paginator(
+            DumbTableModel::select(),
+            1,
+            self::LIMIT
+        );
+    }
+
+    public function testTotalPages()
+    {
+        $this->assertSame(3, $this->paginator->totalPages());
+    }
+
+    public function testGet()
+    {
+        $this->assertSame(self::LIMIT, $this->paginator->get()->count());
+        $this->assertSame(
+            1,
+            (new Paginator(DumbTableModel::select(), 3, self::LIMIT))->get()
+                                                                     ->count()
+        );
+    }
+
+    public function testLinks()
+    {
+        $links = $this->paginator->links(
+            'https://request/url/',
+            ['unrelated' => 1, 'query' => 2, 'params' => 3]
+        );
+        $this->assertStringContainsString('page=1', $links['first']);
+        $this->assertStringContainsString('page=3', $links['last']);
+        $this->assertSame(null, $links['prev']);
+        $this->assertStringContainsString('page=2', $links['next']);
+        $this->assertStringContainsString('unrelated=1', $links['next']);
+
+        $lastPagePaginator = new Paginator(
+            DumbTableModel::select(),
+            3,
+            self::LIMIT
+        );
+        $this->assertSame(
+            null,
+            $lastPagePaginator->links('https://request/url/')['next']
+        );
+    }
+}

+ 13 - 0
tests/TestCase.php

@@ -22,4 +22,17 @@ abstract class TestCase extends BaseTestCase
     {
         return app('db')->connection()->getSchemaBuilder();
     }
+
+    /**
+     * I don't want it fluent.
+     */
+    public function request(
+        $method,
+        $uri,
+        array $data = [],
+        array $headers = []
+    ) {
+        $this->json($method, $uri, $data, $headers);
+        return $this->response;
+    }
 }

+ 14 - 0
tests/UserTest.php

@@ -34,6 +34,13 @@ class UserTest extends TestCase
         );
     }
 
+    public function testLogin()
+    {
+        $this->assertNull(app('session')->get('user_uid'));
+        $this->user->login();
+        $this->assertSame($this->user->uid, app('session')->get('user_uid'));
+    }
+
     public function testLoginByPassword()
     {
         $_SERVER['REMOTE_ADDR'] = '127.0.0.1';
@@ -190,6 +197,13 @@ class UserTest extends TestCase
         User::emailToken($this->user->uid, 'not_origin');
     }
 
+    public function testAmI()
+    {
+        $this->assertFalse($this->user->amI());
+        app('session')->set('user_uid', $this->user->uid);
+        $this->assertTrue($this->user->amI());
+    }
+
     public function testIsOwner()
     {
         $this->assertTrue($this->user->isOwner($this->user));