From 6eda1c1fb28142d432bf0ca05d7eaba9bf16f0d0 Mon Sep 17 00:00:00 2001
From: Dan Brown <ssddanbrown@googlemail.com>
Date: Sun, 17 Jan 2021 13:21:57 +0000
Subject: [PATCH] Added status endpoint

For #2467
---
 app/Http/Controllers/StatusController.php | 47 +++++++++++++++++++
 routes/web.php                            |  1 +
 tests/StatusTest.php                      | 56 +++++++++++++++++++++++
 version                                   |  2 +-
 4 files changed, 105 insertions(+), 1 deletion(-)
 create mode 100644 app/Http/Controllers/StatusController.php
 create mode 100644 tests/StatusTest.php

diff --git a/app/Http/Controllers/StatusController.php b/app/Http/Controllers/StatusController.php
new file mode 100644
index 000000000..9f4ed4d89
--- /dev/null
+++ b/app/Http/Controllers/StatusController.php
@@ -0,0 +1,47 @@
+<?php namespace BookStack\Http\Controllers;
+
+use Illuminate\Support\Facades\Cache;
+use Illuminate\Support\Facades\DB;
+use Illuminate\Support\Facades\Session;
+use Illuminate\Support\Str;
+
+class StatusController extends Controller
+{
+
+    /**
+     * Show the system status as a simple json page.
+     */
+    public function show()
+    {
+        $statuses = [
+            'database' => $this->trueWithoutError(function () {
+                return DB::table('migrations')->count() > 0;
+            }),
+            'cache' => $this->trueWithoutError(function () {
+                $rand = Str::random();
+                Cache::set('status_test', $rand);
+                return Cache::get('status_test') === $rand;
+            }),
+            'session' => $this->trueWithoutError(function () {
+                $rand = Str::random();
+                Session::put('status_test', $rand);
+                return Session::get('status_test') === $rand;
+            }),
+        ];
+
+        $hasError = in_array(false, $statuses);
+        return response()->json($statuses, $hasError ? 500 : 200);
+    }
+
+    /**
+     * Check the callable passed returns true and does not throw an exception.
+     */
+    protected function trueWithoutError(callable $test): bool
+    {
+        try {
+            return $test() === true;
+        } catch (\Exception $e) {
+            return false;
+        }
+    }
+}
diff --git a/routes/web.php b/routes/web.php
index e8f217862..64d08e165 100644
--- a/routes/web.php
+++ b/routes/web.php
@@ -1,5 +1,6 @@
 <?php
 
+Route::get('/status', 'StatusController@show');
 Route::get('/robots.txt', 'HomeController@getRobots');
 
 // Authenticated routes...
diff --git a/tests/StatusTest.php b/tests/StatusTest.php
new file mode 100644
index 000000000..ca150d0d6
--- /dev/null
+++ b/tests/StatusTest.php
@@ -0,0 +1,56 @@
+<?php
+
+use Illuminate\Support\Facades\DB;
+use Illuminate\Support\Facades\Cache;
+use Illuminate\Support\Facades\Session;
+use Tests\TestCase;
+
+class StatusTest extends TestCase
+{
+    public function test_returns_json_with_expected_results()
+    {
+        $resp = $this->get("/status");
+        $resp->assertStatus(200);
+        $resp->assertJson([
+            'database' => true,
+            'cache' => true,
+            'session' => true,
+        ]);
+    }
+
+    public function test_returns_500_status_and_false_on_db_error()
+    {
+        DB::shouldReceive('table')->andThrow(new Exception());
+
+        $resp = $this->get("/status");
+        $resp->assertStatus(500);
+        $resp->assertJson([
+            'database' => false,
+        ]);
+    }
+
+    public function test_returns_500_status_and_false_on_wrong_cache_return()
+    {
+        Cache::partialMock()->shouldReceive('get')->andReturn('cat');
+
+        $resp = $this->get("/status");
+        $resp->assertStatus(500);
+        $resp->assertJson([
+            'cache' => false,
+        ]);
+    }
+
+    public function test_returns_500_status_and_false_on_wrong_session_return()
+    {
+        $session = Session::getFacadeRoot();
+        $mockSession = Mockery::mock($session)->makePartial();
+        Session::swap($mockSession);
+        $mockSession->shouldReceive('get')->andReturn('cat');
+
+        $resp = $this->get("/status");
+        $resp->assertStatus(500);
+        $resp->assertJson([
+            'session' => false,
+        ]);
+    }
+}
\ No newline at end of file
diff --git a/version b/version
index 31c2e1d4b..92d9faea7 100644
--- a/version
+++ b/version
@@ -1 +1 @@
-v0.30-dev
+v0.32-dev