From d3709de035bcb3fbf8440a79ac7a5df8331333a9 Mon Sep 17 00:00:00 2001
From: Dan Brown <ssddanbrown@googlemail.com>
Date: Sat, 2 Jan 2016 14:48:35 +0000
Subject: [PATCH] Added more tests to increase test coverage

---
 app/Entity.php                            | 15 +-----
 app/Http/Controllers/SearchController.php |  6 +--
 app/Http/Controllers/UserController.php   |  7 ++-
 app/Role.php                              | 12 ++++-
 database/seeds/DummyContentSeeder.php     |  2 +-
 phpunit.xml                               |  2 +-
 resources/assets/js/controllers.js        |  4 +-
 resources/views/users/edit.blade.php      |  2 +-
 tests/AuthTest.php                        | 57 +++++++++++++++++++----
 tests/EntityTest.php                      | 23 +++++++++
 10 files changed, 97 insertions(+), 33 deletions(-)

diff --git a/app/Entity.php b/app/Entity.php
index 3d1c4ad58..705444959 100644
--- a/app/Entity.php
+++ b/app/Entity.php
@@ -31,11 +31,7 @@ abstract class Entity extends Model
 
         if ($matches) return true;
 
-        if ($entity->isA('chapter') && $this->isA('book')) {
-            return $entity->book_id === $this->id;
-        }
-
-        if ($entity->isA('page') && $this->isA('book')) {
+        if (($entity->isA('chapter') || $entity->isA('page')) && $this->isA('book')) {
             return $entity->book_id === $this->id;
         }
 
@@ -64,15 +60,6 @@ abstract class Entity extends Model
         return $this->morphMany('BookStack\View', 'viewable');
     }
 
-    /**
-     * Get just the views for the current user.
-     * @return mixed
-     */
-    public function userViews()
-    {
-        return $this->views()->where('user_id', '=', auth()->user()->id);
-    }
-
     /**
      * Allows checking of the exact class, Used to check entity type.
      * Cleaner method for is_a.
diff --git a/app/Http/Controllers/SearchController.php b/app/Http/Controllers/SearchController.php
index c9ca1f09f..035de9fe6 100644
--- a/app/Http/Controllers/SearchController.php
+++ b/app/Http/Controllers/SearchController.php
@@ -62,9 +62,9 @@ class SearchController extends Controller
             return redirect()->back();
         }
         $searchTerm = $request->get('term');
-        $whereTerm = [['book_id', '=', $bookId]];
-        $pages = $this->pageRepo->getBySearch($searchTerm, $whereTerm);
-        $chapters = $this->chapterRepo->getBySearch($searchTerm, $whereTerm);
+        $searchWhereTerms = [['book_id', '=', $bookId]];
+        $pages = $this->pageRepo->getBySearch($searchTerm, $searchWhereTerms);
+        $chapters = $this->chapterRepo->getBySearch($searchTerm, $searchWhereTerms);
         return view('search/book', ['pages' => $pages, 'chapters' => $chapters, 'searchTerm' => $searchTerm]);
     }
 
diff --git a/app/Http/Controllers/UserController.php b/app/Http/Controllers/UserController.php
index fe25c44ae..b81be16f6 100644
--- a/app/Http/Controllers/UserController.php
+++ b/app/Http/Controllers/UserController.php
@@ -116,9 +116,11 @@ class UserController extends Controller
         $this->validate($request, [
             'name'             => 'required',
             'email'            => 'required|email|unique:users,email,' . $id,
-            'password'         => 'min:5',
-            'password-confirm' => 'same:password',
+            'password'         => 'min:5|required_with:password_confirm',
+            'password-confirm' => 'same:password|required_with:password',
             'role'             => 'exists:roles,id'
+        ], [
+            'password-confirm.required_with' => 'Password confirmation required'
         ]);
 
         $user = $this->user->findOrFail($id);
@@ -132,6 +134,7 @@ class UserController extends Controller
             $password = $request->get('password');
             $user->password = bcrypt($password);
         }
+
         $user->save();
         return redirect('/users');
     }
diff --git a/app/Role.php b/app/Role.php
index 3e58164c5..c698a1cf6 100644
--- a/app/Role.php
+++ b/app/Role.php
@@ -43,6 +43,16 @@ class Role extends Model
      */
     public static function getDefault()
     {
-        return static::where('name', '=', static::$default)->first();
+        return static::getRole(static::$default);
+    }
+
+    /**
+     * Get the role object for the specified role.
+     * @param $roleName
+     * @return mixed
+     */
+    public static function getRole($roleName)
+    {
+        return static::where('name', '=', $roleName)->first();
     }
 }
diff --git a/database/seeds/DummyContentSeeder.php b/database/seeds/DummyContentSeeder.php
index d2ccc7960..aa70eaa0a 100644
--- a/database/seeds/DummyContentSeeder.php
+++ b/database/seeds/DummyContentSeeder.php
@@ -12,7 +12,7 @@ class DummyContentSeeder extends Seeder
     public function run()
     {
         $user = factory(BookStack\User::class, 1)->create();
-        $role = \BookStack\Role::where('name', '=', 'admin')->first();
+        $role = \BookStack\Role::getDefault();
         $user->attachRole($role);
 
 
diff --git a/phpunit.xml b/phpunit.xml
index d86aacd00..1704159e2 100644
--- a/phpunit.xml
+++ b/phpunit.xml
@@ -26,6 +26,6 @@
         <env name="QUEUE_DRIVER" value="sync"/>
         <env name="DB_CONNECTION" value="mysql_testing"/>
         <env name="MAIL_PRETEND" value="true"/>
-        <env name="DISABLE_EXTERNAL_SERVICES" value="true"/>
+        <env name="DISABLE_EXTERNAL_SERVICES" value="false"/>
     </php>
 </phpunit>
diff --git a/resources/assets/js/controllers.js b/resources/assets/js/controllers.js
index 5cf71579a..5d8990754 100644
--- a/resources/assets/js/controllers.js
+++ b/resources/assets/js/controllers.js
@@ -127,7 +127,7 @@ module.exports = function (ngApp) {
         }]);
 
 
-    ngApp.controller('BookShowController', ['$scope', '$http', '$attrs', function ($scope, $http, $attrs) {
+    ngApp.controller('BookShowController', ['$scope', '$http', '$attrs', '$sce', function ($scope, $http, $attrs, $sce) {
         $scope.searching = false;
         $scope.searchTerm = '';
         $scope.searchResults = '';
@@ -141,7 +141,7 @@ module.exports = function (ngApp) {
             var searchUrl = '/search/book/' + $attrs.bookId;
             searchUrl += '?term=' + encodeURIComponent(term);
             $http.get(searchUrl).then((response) => {
-                $scope.searchResults = response.data;
+                $scope.searchResults = $sce.trustAsHtml(response.data);
             });
         };
 
diff --git a/resources/views/users/edit.blade.php b/resources/views/users/edit.blade.php
index ca575731e..e7100bdb1 100644
--- a/resources/views/users/edit.blade.php
+++ b/resources/views/users/edit.blade.php
@@ -9,7 +9,7 @@
                 <div class="col-md-6"></div>
                 <div class="col-md-6 faded">
                     <div class="action-buttons">
-                        <a href="/users/{{$user->id}}/delete" class="text-neg text-button"><i class="zmdi zmdi-delete"></i>Delete user</a>
+                        <a href="/users/{{$user->id}}/delete" class="text-neg text-button"><i class="zmdi zmdi-delete"></i>Delete User</a>
                     </div>
                 </div>
             </div>
diff --git a/tests/AuthTest.php b/tests/AuthTest.php
index befa0214d..3faae6506 100644
--- a/tests/AuthTest.php
+++ b/tests/AuthTest.php
@@ -102,10 +102,10 @@ class AuthTest extends TestCase
             ->seeInDatabase('users', ['name' => $user->name, 'email' => $user->email, 'email_confirmed' => true]);
     }
 
-    public function testUserControl()
+    public function testUserCreation()
     {
         $user = factory(\BookStack\User::class)->make();
-        // Test creation
+
         $this->asAdmin()
             ->visit('/users')
             ->click('Add new user')
@@ -118,9 +118,12 @@ class AuthTest extends TestCase
             ->seeInDatabase('users', $user->toArray())
             ->seePageIs('/users')
             ->see($user->name);
-        $user = $user->where('email', '=', $user->email)->first();
+    }
 
-        // Test editing
+    public function testUserUpdating()
+    {
+        $user = \BookStack\User::all()->last();
+        $password = $user->password;
         $this->asAdmin()
             ->visit('/users')
             ->click($user->name)
@@ -129,20 +132,58 @@ class AuthTest extends TestCase
             ->type('Barry Scott', '#name')
             ->press('Save')
             ->seePageIs('/users')
-            ->seeInDatabase('users', ['id' => $user->id, 'name' => 'Barry Scott'])
+            ->seeInDatabase('users', ['id' => $user->id, 'name' => 'Barry Scott', 'password' => $password])
             ->notSeeInDatabase('users', ['name' => $user->name]);
-        $user = $user->find($user->id);
+    }
+
+    public function testUserPasswordUpdate()
+    {
+        $user = \BookStack\User::all()->last();
+        $userProfilePage = '/users/' . $user->id;
+        $this->asAdmin()
+            ->visit($userProfilePage)
+            ->type('newpassword', '#password')
+            ->press('Save')
+            ->seePageIs($userProfilePage)
+            ->see('Password confirmation required')
+
+            ->type('newpassword', '#password')
+            ->type('newpassword', '#password-confirm')
+            ->press('Save')
+            ->seePageIs('/users');
+
+            $userPassword = \BookStack\User::find($user->id)->password;
+            $this->assertTrue(Hash::check('newpassword', $userPassword));
+    }
+
+    public function testUserDeletion()
+    {
+        $userDetails = factory(\BookStack\User::class)->make();
+        $user = $this->getNewUser($userDetails->toArray());
 
-        // Test Deletion
         $this->asAdmin()
             ->visit('/users/' . $user->id)
-            ->click('Delete user')
+            ->click('Delete User')
             ->see($user->name)
             ->press('Confirm')
             ->seePageIs('/users')
             ->notSeeInDatabase('users', ['name' => $user->name]);
     }
 
+    public function testUserCannotBeDeletedIfLastAdmin()
+    {
+        $adminRole = \BookStack\Role::getRole('admin');
+        // Ensure we currently only have 1 admin user
+        $this->assertEquals(1, $adminRole->users()->count());
+        $user = $adminRole->users->first();
+
+        $this->asAdmin()->visit('/users/' . $user->id)
+            ->click('Delete User')
+            ->press('Confirm')
+            ->seePageIs('/users/' . $user->id)
+            ->see('You cannot delete the only admin');
+    }
+
     public function testLogout()
     {
         $this->asAdmin()
diff --git a/tests/EntityTest.php b/tests/EntityTest.php
index 4c4195fd9..1eda7c73c 100644
--- a/tests/EntityTest.php
+++ b/tests/EntityTest.php
@@ -188,6 +188,29 @@ class EntityTest extends TestCase
             ->seePageIs('/');
     }
 
+    public function testBookSearch()
+    {
+        $book = \BookStack\Book::all()->first();
+        $page = $book->pages->last();
+        $chapter = $book->chapters->last();
+
+        $this->asAdmin()
+            ->visit('/search/book/' . $book->id . '?term=' . urlencode($page->name))
+            ->see($page->name)
+
+            ->visit('/search/book/' . $book->id  . '?term=' . urlencode($chapter->name))
+            ->see($chapter->name);
+    }
+
+    public function testEmptyBookSearchRedirectsBack()
+    {
+        $book = \BookStack\Book::all()->first();
+        $this->asAdmin()
+            ->visit('/books')
+            ->visit('/search/book/' . $book->id . '?term=')
+            ->seePageIs('/books');
+    }
+
 
     public function testEntitiesViewableAfterCreatorDeletion()
     {