Skip to content

Commit

Permalink
Merge branch 'master' into master
Browse files Browse the repository at this point in the history
  • Loading branch information
robinostlund authored Feb 18, 2022
2 parents 8f48a94 + bdbcabc commit 7fc3ad8
Show file tree
Hide file tree
Showing 17 changed files with 637 additions and 204 deletions.
21 changes: 19 additions & 2 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,37 @@
# See https://pre-commit.com/hooks.html for more hooks
default_language_version:
python: python3.9

repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.1.0
hooks:
- id: trailing-whitespace
- id: end-of-file-fixer
- id: check-json
- id: check-toml
- id: check-yaml
- id: check-added-large-files
- id: debug-statements
- id: end-of-file-fixer
- id: no-commit-to-branch
args: ['--branch', 'main', '--branch', 'master']
- id: requirements-txt-fixer
- id: trailing-whitespace

- repo: https://github.com/psf/black
rev: '22.1.0'
hooks:
- id: black

- repo: https://github.com/pycqa/flake8
rev: '4.0.1'
hooks:
- id: flake8
additional_dependencies: [ flake8-docstrings ]

- repo: https://github.com/asottile/pyupgrade
rev: v2.31.0
hooks:
- id: pyupgrade
args: [
'--py37-plus'
]
3 changes: 1 addition & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,7 @@
requires = [
"setuptools>=45",
"wheel>=0.37.0",
"setuptools_scm>=6.x",
"pytest>=6,<8",
"setuptools_scm>=6.0",
]
build-backend = "setuptools.build_meta"

Expand Down
10 changes: 5 additions & 5 deletions requirements-test.txt
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
-r requirements.txt
pytest>=7.0.0
setuptools
pytest-asyncio
black
flake8
flake8-docstrings
freezegun>=1.0.0
pre-commit
pytest>=7.0.0
pytest-asyncio
pytest-cov
pytest-subtests
black
freezegun>=1.0.0
setuptools
4 changes: 2 additions & 2 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
lxml
beautifulsoup4
aiohttp
beautifulsoup4
lxml
pyjwt
1 change: 1 addition & 0 deletions tests/conftest.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
"""Configure tests."""
import sys

pytest_plugins = ["pytest_cov"]
Expand Down
7 changes: 3 additions & 4 deletions tests/dummy_test.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,14 @@
"""Dummy tests. Might be removed once there are proper ones."""
import pytest
from aiohttp import ClientSession

# we need to change os path to be able to import volkswagecarnet
from volkswagencarnet import vw_connection


@pytest.mark.asyncio
async def test_volkswagencarnet():
"""Dummy test to ensure logged in status is false by default."""
async with ClientSession() as session:
connection = vw_connection.Connection(session, "[email protected]", "test_password")
# if await connection._login():
if not connection.logged_in:
return True
pytest.fail("Something happend we should have got a False from vw.logged_in")
assert connection.logged_in is False
5 changes: 3 additions & 2 deletions tests/fixtures/connection.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
"""Session and connection related test fixtures."""
import os
from pathlib import Path

Expand All @@ -13,7 +14,7 @@

@pytest_asyncio.fixture
async def session():
"""Client session that can be used in tests"""
"""Client session that can be used in tests."""
jar = CookieJar()
jar.load(os.path.join(resource_path, "dummy_cookies.pickle"))
sess = ClientSession(headers={"Connection": "keep-alive"}, cookie_jar=jar)
Expand All @@ -23,5 +24,5 @@ async def session():

@pytest.fixture
def connection(session):
"""Real connection for integration tests"""
"""Real connection for integration tests."""
return Connection(session=session, username="", password="", country="DE", interval=999, fulldebug=True)
29 changes: 25 additions & 4 deletions tests/integration_test.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,12 @@
"""
Integration tests.
These tests use actual credentials, and should thus be used with care.
Credentials have to be specified in credentials.py.
"""
import logging
from unittest import skip

import pytest
from aiohttp import ClientSession

Expand All @@ -7,17 +16,29 @@
from credentials import username, password, spin, vin
except ImportError:
username = password = spin = vin = None
pass


@pytest.mark.skipif(
username is None or password is None, reason="Username or password is not set. Check credentials.py.sample"
)
@pytest.mark.asyncio
async def test_successful_login():
"""Test that login succeeds."""
async with ClientSession() as session:
connection = vw_connection.Connection(session, username, password)
await connection.doLogin()
if connection.logged_in:
return True
pytest.fail("Login failed")
assert connection.logged_in is True


@pytest.mark.skipif(
username is None or password is None, reason="Username or password is not set. Check credentials.py.sample"
)
@pytest.mark.asyncio
@skip("Not yet implemented")
async def test_spin_action():
"""
Test something that uses s-pin.
Not yet implemented...
"""
logging.getLogger().debug(f"using vin: {vin} and s-pin: {spin}")
30 changes: 14 additions & 16 deletions tests/vw_connection_test.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
"""Tests for main connection class."""
import sys
import unittest

from volkswagencarnet import vw_connection

if sys.version_info >= (3, 8):
# This won't work on python versions less than 3.8
from unittest import IsolatedAsyncioTestCase
else:
from unittest import TestCase

class IsolatedAsyncioTestCase(unittest.TestCase):
class IsolatedAsyncioTestCase(TestCase):
"""Dummy class to use instead (tests might need to skipped separately also)."""

pass
Expand All @@ -18,10 +20,6 @@ class IsolatedAsyncioTestCase(unittest.TestCase):

import pytest

import volkswagencarnet.vw_connection
from volkswagencarnet.vw_connection import Connection
from volkswagencarnet.vw_vehicle import Vehicle


@pytest.mark.skipif(condition=sys.version_info < (3, 8), reason="Test incompatible with Python < 3.8")
def test_clear_cookies(connection):
Expand Down Expand Up @@ -63,13 +61,13 @@ async def update(self):
@property
def vehicles(self):
"""Return the vehicles."""
vehicle1 = Vehicle(None, "vin1")
vehicle2 = Vehicle(None, "vin2")
vehicle1 = vw_connection.Vehicle(None, "vin1")
vehicle2 = vw_connection.Vehicle(None, "vin2")
return [vehicle1, vehicle2]

@pytest.mark.asyncio
@patch.object(volkswagencarnet.vw_connection.logging, "basicConfig")
@patch("volkswagencarnet.vw_connection.Connection", spec_set=Connection, new=FailingLoginConnection)
@patch.object(vw_connection.logging, "basicConfig")
@patch("volkswagencarnet.vw_connection.Connection", spec_set=vw_connection.Connection, new=FailingLoginConnection)
@pytest.mark.skipif(condition=sys.version_info < (3, 8), reason="Test incompatible with Python < 3.8")
async def test_main_argv(self, logger_config):
"""Test verbosity flags."""
Expand All @@ -86,27 +84,27 @@ async def test_main_argv(self, logger_config):
for c in cases:
args = ["dummy"]
args.extend(c[1])
with patch.object(volkswagencarnet.vw_connection.sys, "argv", args), self.subTest(msg=c[0]):
await volkswagencarnet.vw_connection.main()
with patch.object(vw_connection.sys, "argv", args), self.subTest(msg=c[0]):
await vw_connection.main()
logger_config.assert_called_with(level=c[2])
logger_config.reset()

@pytest.mark.asyncio
@patch("sys.stdout", new_callable=StringIO)
@patch("volkswagencarnet.vw_connection.Connection", spec_set=Connection, new=FailingLoginConnection)
@patch("volkswagencarnet.vw_connection.Connection", spec_set=vw_connection.Connection, new=FailingLoginConnection)
@pytest.mark.skipif(condition=sys.version_info < (3, 8), reason="Test incompatible with Python < 3.8")
async def test_main_output_failed(self, stdout: StringIO):
"""Verify empty stdout on failed login."""
await volkswagencarnet.vw_connection.main()
await vw_connection.main()
assert stdout.getvalue() == ""

@pytest.mark.asyncio
@patch("sys.stdout", new_callable=StringIO)
@patch("volkswagencarnet.vw_connection.Connection", spec_set=Connection, new=TwoVehiclesConnection)
@patch("volkswagencarnet.vw_connection.Connection", spec_set=vw_connection.Connection, new=TwoVehiclesConnection)
@pytest.mark.skipif(condition=sys.version_info < (3, 8), reason="Test incompatible with Python < 3.8")
async def test_main_output_two_vehicles(self, stdout: StringIO):
"""Get console output for two vehicles."""
await volkswagencarnet.vw_connection.main()
await vw_connection.main()
assert (
stdout.getvalue()
== """Vehicle id: vin1
Expand Down
13 changes: 13 additions & 0 deletions tests/vw_exceptions_test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
"""Misc tests for exception and their handling."""
from unittest import TestCase

from volkswagencarnet.vw_exceptions import AuthenticationException


class ExceptionTests(TestCase):
"""Unit tests for exceptions."""

def test_auth_exception(self):
"""Test that message matches. Dummy test."""
ex = AuthenticationException("foo failed")
self.assertEqual("foo failed", ex.__str__())
5 changes: 2 additions & 3 deletions tests/vw_utilities_test.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
import unittest
from datetime import datetime, timezone, timedelta
from json import JSONDecodeError
from unittest import mock
from unittest import TestCase, mock
from unittest.mock import DEFAULT

from volkswagencarnet.vw_utilities import camel2slug, is_valid_path, obj_parser, json_loads, read_config


class UtilitiesTest(unittest.TestCase):
class UtilitiesTest(TestCase):
def test_camel_to_slug(self):
data = {"foo": "foo", "fooBar": "foo_bar", "XYZ": "x_y_z", "B4R": "b4_r"} # Should this actually be "b_4_r"? =)
for v in data:
Expand Down
29 changes: 25 additions & 4 deletions tests/vw_vehicle_test.py
Original file line number Diff line number Diff line change
@@ -1,17 +1,20 @@
"""Vehicle class tests."""
import sys
import unittest
from datetime import datetime

# This won't work on python versions less than 3.8
if sys.version_info >= (3, 8):
# This won't work on python versions less than 3.8
from unittest import IsolatedAsyncioTestCase
else:
from unittest import TestCase

class IsolatedAsyncioTestCase(TestCase):
"""Python 3.7 compatibility dummy class."""

class IsolatedAsyncioTestCase(unittest.TestCase):
pass


from unittest.mock import MagicMock
from unittest.mock import MagicMock, patch

from aiohttp import ClientSession
from freezegun import freeze_time
Expand All @@ -20,8 +23,11 @@ class IsolatedAsyncioTestCase(unittest.TestCase):


class VehicleTest(IsolatedAsyncioTestCase):
"""Test Vehicle methods."""

@freeze_time("2022-02-14 03:04:05")
async def test_init(self):
"""Test that init does what it should."""
async with ClientSession() as conn:
target_date = datetime.fromisoformat("2022-02-14 03:04:05")
url = "https://foo.bar"
Expand Down Expand Up @@ -63,9 +69,11 @@ async def test_init(self):
)

def test_discover(self):
"""Test the discovery process."""
pass

async def test_update_deactivated(self):
"""Test that calling update on a deactivated Vehicle does nothing."""
vehicle = MagicMock(spec=Vehicle, name="MockDeactivatedVehicle")
vehicle.update = lambda: Vehicle.update(vehicle)
vehicle._discovered = True
Expand All @@ -78,6 +86,7 @@ async def test_update_deactivated(self):
self.assertEqual(0, vehicle.method_calls.__len__(), f"xpected none, got {vehicle.method_calls}")

async def test_update(self):
"""Test that update calls the wanted methods and nothing else."""
vehicle = MagicMock(spec=Vehicle, name="MockUpdateVehicle")
vehicle.update = lambda: Vehicle.update(vehicle)

Expand All @@ -98,3 +107,15 @@ async def test_update(self):
self.assertEqual(
8, vehicle.method_calls.__len__(), f"Wrong number of methods called. Expected 8, got {vehicle.method_calls}"
)

async def test_json(self):
"""Test that update calls the wanted methods and nothing else."""
vehicle = Vehicle(conn=None, url="dummy34")

vehicle._discovered = True
dtstring = "2022-02-22T02:22:20+02:00"
d = datetime.fromisoformat(dtstring)

with patch.dict(vehicle.attrs, {"a string": "yay", "some date": d}):
res = f"{vehicle.json}"
self.assertEqual('{\n "a string": "yay",\n "some date": "2022-02-22T02:22:20+02:00"\n}', res)
Loading

0 comments on commit 7fc3ad8

Please sign in to comment.