From 9aa7ef1912edf4d5ffe9b7054377ea1a88411348 Mon Sep 17 00:00:00 2001
From: Nico Alt <nicoalt@posteo.org>
Date: Mon, 8 Jun 2020 12:00:00 +0000
Subject: [PATCH] Add API tests

---
 briar_wrapper/api.py            |   6 +-
 tests/briar_wrapper/test_api.py | 168 ++++++++++++++++++++++++++++++++
 tests/conftest.py               |   6 +-
 3 files changed, 173 insertions(+), 7 deletions(-)
 create mode 100644 tests/briar_wrapper/test_api.py

diff --git a/briar_wrapper/api.py b/briar_wrapper/api.py
index 6df2ec8..8804ae6 100644
--- a/briar_wrapper/api.py
+++ b/briar_wrapper/api.py
@@ -2,7 +2,7 @@
 # SPDX-License-Identifier: AGPL-3.0-only
 # License-Filename: LICENSE.md
 
-from os.path import isfile
+import os
 from subprocess import Popen, PIPE, STDOUT
 from threading import Thread
 from time import sleep
@@ -25,7 +25,7 @@ class Api:
 
     @staticmethod
     def has_account():
-        return isfile(BRIAR_DB)
+        return os.path.isfile(BRIAR_DB)
 
     def is_running(self):
         return (self._process is not None) and (self._process.poll() is None)
@@ -79,7 +79,7 @@ class Api:
     def _login(self, password):
         if not self.is_running():
             raise Exception("Can't login; API not running")
-        self._process.communicate(("%s\n" % password).encode("utf-8"))
+        self._process.communicate((f"{password}\n").encode("utf-8"))
 
     def _register(self, credentials):
         if not self.is_running():
diff --git a/tests/briar_wrapper/test_api.py b/tests/briar_wrapper/test_api.py
new file mode 100644
index 0000000..3566e77
--- /dev/null
+++ b/tests/briar_wrapper/test_api.py
@@ -0,0 +1,168 @@
+# Copyright (c) 2019 Nico Alt
+# SPDX-License-Identifier: AGPL-3.0-only
+# License-Filename: LICENSE.md
+
+import pytest
+import subprocess
+
+from briar_wrapper.api import Api
+from briar_wrapper.constants import BRIAR_DB
+
+HEADLESS_JAR = 'briar-headless.jar'
+PASSWORD = 'LjnM6/WPQ]V?@<=$'
+CREDENTIALS = ('Alice', PASSWORD)
+
+
+def test_has_account(mocker):
+    isfile_mock = mocker.patch('os.path.isfile')
+    isfile_mock.return_value = False
+    api = Api(HEADLESS_JAR)
+
+    assert api.has_account() is False
+    isfile_mock.assert_called_once_with(BRIAR_DB)
+
+
+def test_is_running(mocker, process):
+    process.poll.return_value = None
+    api = Api(HEADLESS_JAR)
+    api._process = process
+
+    assert api.is_running() is True
+
+
+def test_is_running_none():
+    api = Api(HEADLESS_JAR)
+    api._process = None
+
+    assert api.is_running() is False
+
+
+def test_is_running_poll_none(mocker, process):
+    process.poll.return_value = 0
+    api = Api(HEADLESS_JAR)
+    api._process = process
+
+    assert api.is_running() is False
+
+
+def test_login(callback, start_and_watch, thread):
+    api = Api(HEADLESS_JAR)
+
+    api.login(PASSWORD, callback)
+
+    start_and_watch.assert_called_once_with(callback)
+    thread.assert_called_once_with(target=api._login, args=(PASSWORD,),
+                                   daemon=True)
+
+
+def test_login_already_running(callback, is_running, thread):
+    api = Api(HEADLESS_JAR)
+
+    with pytest.raises(Exception, match='API already running'):
+        api.login(PASSWORD, callback)
+
+
+def test_login_not_running():
+    # TODO: Write test for failed login due to API not running
+    # Not easy to test because exception is thrown in Thread
+    pass
+
+
+def test_login_communicate(callback, is_running, mocker,
+                           process, start_and_watch):
+    api = Api(HEADLESS_JAR)
+    api._process = process
+
+    api.login(PASSWORD, callback)
+
+    process.communicate.assert_called_once_with(
+        (PASSWORD + "\n").encode("utf-8")
+    )
+
+
+def test_register(callback, start_and_watch, thread):
+    api = Api(HEADLESS_JAR)
+
+    api.register(CREDENTIALS, callback)
+
+    start_and_watch.assert_called_once_with(callback)
+    thread.assert_called_once_with(target=api._register, args=(CREDENTIALS,),
+                                   daemon=True)
+
+
+def test_register_already_running(callback, is_running, thread):
+    api = Api(HEADLESS_JAR)
+
+    with pytest.raises(Exception, match='API already running'):
+        api.register(CREDENTIALS, callback)
+
+
+def test_register_invalid_credentials(callback):
+    api = Api(HEADLESS_JAR)
+
+    with pytest.raises(Exception, match="Can't process credentials"):
+        api.register(PASSWORD, callback)
+
+
+def test_register_communicate(callback, is_running, mocker,
+                              process, start_and_watch):
+    api = Api(HEADLESS_JAR)
+    api._process = process
+
+    api.register(CREDENTIALS, callback)
+
+    process.communicate.assert_called_once_with(
+        (CREDENTIALS[0] + '\n' +
+         CREDENTIALS[1] + '\n' +
+         CREDENTIALS[1] + '\n').encode("utf-8")
+    )
+
+
+def test_stop(mocker, is_running, process):
+    api = Api(HEADLESS_JAR)
+    api._process = process
+
+    api.stop()
+
+    api._process.terminate.assert_called_once()
+
+
+def test_stop_not_running():
+    api = Api(HEADLESS_JAR)
+
+    with pytest.raises(Exception, match='Nothing to stop'):
+        api.stop()
+
+
+def test_start_and_watch():
+    # TODO: Various tests needed here, for both register and login
+    pass
+
+
+@pytest.fixture
+def callback(mocker):
+    return mocker.MagicMock()
+
+
+@pytest.fixture
+def is_running(mocker):
+    is_running_mock = mocker.patch(
+        'briar_wrapper.api.Api.is_running'
+        )
+    is_running_mock.return_value = True
+    return is_running_mock
+
+
+@pytest.fixture
+def process(mocker):
+    return mocker.MagicMock()
+
+
+@pytest.fixture
+def start_and_watch(mocker):
+    return mocker.patch('briar_wrapper.api.Api._start_and_watch')
+
+
+@pytest.fixture
+def thread(mocker):
+    return mocker.patch('briar_wrapper.api.Thread')
diff --git a/tests/conftest.py b/tests/conftest.py
index 9310856..91a2eb2 100644
--- a/tests/conftest.py
+++ b/tests/conftest.py
@@ -2,16 +2,14 @@
 # SPDX-License-Identifier: AGPL-3.0-only
 # License-Filename: LICENSE.md
 
-from unittest import mock
-
 import pytest
 
 # pylint: disable=redefined-outer-name
 
 
 @pytest.fixture
-def api(auth_token):
-    api = mock.Mock()
+def api(auth_token, mocker):
+    api = mocker.Mock()
     api.auth_token = auth_token
     return api
 
-- 
GitLab