Repository: leikoilja/glocaltokens (language: python)

Issue:
Add testing environment and unit tests 
**Is your feature request related to a problem? Please describe.**
Now that the package is slowly growing and more people contributing we need to setup testing environment with unit tests. 

**Describe the solution you'd like**
Preferably `tox`


Discussion / hints:

Fix patch:
diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md
index dd84ea7..891c617 100644
--- a/.github/ISSUE_TEMPLATE/bug_report.md
+++ b/.github/ISSUE_TEMPLATE/bug_report.md
@@ -23,16 +23,5 @@ A clear and concise description of what you expected to happen.
 **Screenshots**
 If applicable, add screenshots to help explain your problem.
 
-**Desktop (please complete the following information):**
- - OS: [e.g. iOS]
- - Browser [e.g. chrome, safari]
- - Version [e.g. 22]
-
-**Smartphone (please complete the following information):**
- - Device: [e.g. iPhone6]
- - OS: [e.g. iOS8.1]
- - Browser [e.g. stock browser, safari]
- - Version [e.g. 22]
-
 **Additional context**
 Add any other context about the problem here.
diff --git a/.gitignore b/.gitignore
index d21c922..67ca9e4 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,4 +1,5 @@
 __pycache__/
+.tox/
 .DS_Store
 .env
 /.idea/
@@ -8,3 +9,5 @@ __pycache__/
 *.egg-info
 /build/
 /dist/
+
+venv/
diff --git a/MANIFEST.in b/MANIFEST.in
index e1f325d..becb667 100644
--- a/MANIFEST.in
+++ b/MANIFEST.in
@@ -1,1 +1,2 @@
 include glocaltokens/google/internal/home/foyer/*
+include HISTORY.md
diff --git a/README.md b/README.md
index 87e90b7..a895a61 100644
--- a/README.md
+++ b/README.md
@@ -1,3 +1,4 @@
+[![Glocaltokens Actions Status](https://github.com/leikoilja/glocaltokens/workflows/Running%20tests/badge.svg?branch=master)](https://github.com/leikoilja/glocaltokens/actions)
 # Google home local authentication token extraction
 
 Python 3 package to extract google home devices local authentication tokens from
@@ -45,8 +46,8 @@ print('\n[*] Google devices local authentication tokens')
 google_devices = client.get_google_devices_json()
 ```
 
-# Security Recommendation
-Never store the user's password nor username in plain text, if storage is necessary, generate a 
+## Security Recommendation
+Never store the user's password nor username in plain text, if storage is necessary, generate a
 master token and store it. Example approach:
 ```python
 from glocaltokens.client import GLocalAuthenticationTokens
@@ -67,6 +68,22 @@ print('[*] Master token', master_token)
 
 ```
 
+## Contributing
+This is an open-source project and all countribution is highly welcomed. To
+contribute please:
+- Fork this repo
+- Create a new branch
+- Create a new virtual environment and install dependencies:
+`pip install -r requirements`
+- Implement your changes
+- If possible add tests for your changes
+- Push your changes to your branch
+- Open Pull Request
+
+When writting unittests please follow the good practises like:
+- Use `faker` to fake the data. See [examples](https://faker.readthedocs.io/en/master/)
+- Use `mock` to patch objects/methods. See [examples](https://realpython.com/python-mock-library/)
+
 # Credits
 Much credits go to @rithvikvibhu(https://github.com/rithvikvibhu) for doing
 most of the heavy work like finding a way to extract master and access tokens
diff --git a/requirements b/requirements
new file mode 100644
index 0000000..6c23098
--- /dev/null
+++ b/requirements
@@ -0,0 +1,55 @@
+appdirs==1.4.4
+appnope==0.1.2
+attrs==20.3.0
+backcall==0.2.0
+bleach==3.3.0
+certifi==2020.12.5
+chardet==4.0.0
+colorama==0.4.4
+decorator==4.4.2
+discover==0.4.0
+distlib==0.3.1
+docutils==0.16
+Faker==6.1.1
+filelock==3.0.12
+gpsoauth==0.4.3
+grpcio==1.35.0
+grpcio-tools==1.35.0
+idna==2.10
+iniconfig==1.1.1
+ipdb==0.13.4
+ipython==7.20.0
+ipython-genutils==0.2.0
+jedi==0.18.0
+keyring==22.0.1
+mock==4.0.3
+packaging==20.9
+parso==0.8.1
+pexpect==4.8.0
+pickleshare==0.7.5
+pkginfo==1.7.0
+pluggy==0.13.1
+prompt-toolkit==3.0.14
+protobuf==3.14.0
+ptyprocess==0.7.0
+py==1.10.0
+pycryptodomex==3.10.1
+Pygments==2.7.4
+pyparsing==2.4.7
+python-dateutil==2.8.1
+readme-renderer==28.0
+requests==2.25.1
+requests-toolbelt==0.9.1
+rfc3986==1.4.0
+simplejson==3.17.2
+six==1.15.0
+text-unidecode==1.3
+toml==0.10.2
+tox==3.21.4
+tqdm==4.56.1
+traitlets==5.0.5
+twine==3.3.0
+urllib3==1.26.3
+virtualenv==20.4.2
+wcwidth==0.2.5
+webencodings==0.5.1
diff --git a/tox.ini b/tox.ini
new file mode 100644
index 0000000..cb68e49
--- /dev/null
+++ b/tox.ini
@@ -0,0 +1,14 @@
+[tox]
+; envlist = py27,py36,py37,py38,py39  ; Goal to support these
+envlist = py37,py38
+
+[gh-actions]
+python =
+    3.7: py37
+    3.8: py38
+
+[testenv]
+changedir = tests
+commands =
+    discover
+deps = -rrequirements


Test patch:
diff --git a/.github/workflows/tests.yaml b/.github/workflows/tests.yaml
new file mode 100644
index 0000000..b920cd1
--- /dev/null
+++ b/.github/workflows/tests.yaml
@@ -0,0 +1,25 @@
+name: Running tests
+
+on:
+  - push
+  - pull_request
+
+jobs:
+  build:
+    runs-on: ubuntu-latest
+    strategy:
+      matrix:
+        python-version: [3.7, 3.8]
+
+    steps:
+    - uses: actions/checkout@v1
+    - name: Set up Python ${{ matrix.python-version }}
+      uses: actions/setup-python@v2
+      with:
+        python-version: ${{ matrix.python-version }}
+    - name: Install dependencies
+      run: |
+        python -m pip install --upgrade pip
+        pip install tox tox-gh-actions
+    - name: Test with tox
+      run: tox
diff --git a/tests/__init__.py b/tests/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/tests/test_client.py b/tests/test_client.py
new file mode 100644
index 0000000..9a7f3ce
--- /dev/null
+++ b/tests/test_client.py
@@ -0,0 +1,75 @@
+from faker import Faker
+from mock import patch
+from unittest import TestCase
+
+from glocaltokens.client import GLocalAuthenticationTokens
+
+
+faker = Faker()
+
+
+class GLocalAuthenticationTokensClientTests(TestCase):
+    def setUp(self):
+        """Setup method run before every test"""
+        pass
+
+    def tearDown(self):
+        """Teardown method run after every test"""
+        pass
+
+    def test_initialization(self):
+        username = faker.word()
+        password = faker.word()
+        master_token = faker.word()
+        android_id = faker.word()
+
+        client = GLocalAuthenticationTokens(
+            username=username,
+            password=password,
+            master_token=master_token,
+            android_id=android_id
+        )
+        self.assertEqual(username, client.username)
+        self.assertEqual(password, client.password)
+        self.assertEqual(master_token, client.master_token)
+        self.assertEqual(android_id, client.android_id)
+
+        self.assertIsNone(client.access_token)
+        self.assertIsNone(client.homegraph)
+        self.assertIsNone(client.access_token_date)
+        self.assertIsNone(client.homegraph_date)
+
+    @patch('glocaltokens.client.logging.exception')
+    def test_initialization__valid(self, mock):
+
+        # Without username and password
+        GLocalAuthenticationTokens(
+            username=faker.word(),
+            password=faker.word(),
+        )
+        self.assertEqual(mock.call_count, 0)
+
+        # Without master_token
+        GLocalAuthenticationTokens(
+            master_token=faker.word()
+        )
+        self.assertEqual(mock.call_count, 0)
+
+    @patch('glocaltokens.client.logging.exception')
+    def test_initialization__invalid(self, mock):
+
+        # Without username
+        GLocalAuthenticationTokens(
+            password=faker.word(),
+        )
+        self.assertEqual(mock.call_count, 1)
+
+        # Without password
+        GLocalAuthenticationTokens(
+            username=faker.word(),
+        )
+        self.assertEqual(mock.call_count, 2)
+
+        # Without username and password
+        GLocalAuthenticationTokens()
+        self.assertEqual(mock.call_count, 3)