refactor: fake repo and tesrt coverage

This commit is contained in:
Ruidy 2021-08-04 11:16:01 +02:00
parent 183f739223
commit 6332b9e233
10 changed files with 52 additions and 35 deletions

8
.coveragerc Normal file
View file

@ -0,0 +1,8 @@
[run]
omit =
# omit tests
*/tests/*
# omit non testable files
app/__main__.py
[html]
directory = .htmlcov

2
.gitignore vendored
View file

@ -24,7 +24,7 @@ MANIFEST
*.spec *.spec
pip-log.txt pip-log.txt
pip-delete-this-directory.txt pip-delete-this-directory.txt
htmlcov/ .htmlcov/
.tox/ .tox/
.nox/ .nox/
.coverage .coverage

View file

@ -1,4 +1,4 @@
from .main import app from .main import app
app() # pragma nocover app()

View file

@ -4,7 +4,7 @@ from typing import Any, Protocol
class DBConnector(Protocol): class DBConnector(Protocol):
def commit(self) -> None: def commit(self) -> None:
... ... # pragma nocover
def execute(self, query: str, *args: Any) -> Cursor: def execute(self, query: str, *args: Any) -> Cursor:
... ... # pragma nocover

View file

@ -1,22 +1,21 @@
from __future__ import annotations from __future__ import annotations
from pydantic import SecretStr
from app.models.password import Password from app.models.password import Password
class FakeRepository: class FakeRepository:
_values = {}
def save(self, service: str, password: str) -> None: def save(self, service: str, password: str) -> None:
... self._values[service] = password
def list_all(self) -> list[Password]: def list_all(self) -> list[Password]:
return [ return [
Password(id=0, service="first", password=SecretStr("2yW4AcqG")), Password(id=i, service=v[0], password=v[1]) for i, v in enumerate(self._values.items())
Password(id=1, service="second", password=SecretStr("iK2ZWeqh")),
] ]
def exists(self, service: str) -> bool: def exists(self, service: str) -> bool:
return False return service in self._values.keys()
@classmethod @classmethod
def get_instance(cls) -> FakeRepository: def get_instance(cls) -> FakeRepository:

View file

@ -5,10 +5,10 @@ from app.models.password import Password
class Repository(Protocol): class Repository(Protocol):
def save(self, service: str, password: str) -> None: def save(self, service: str, password: str) -> None:
... ... # pragma nocover
def list_all(self) -> list[Password]: def list_all(self) -> list[Password]:
... ... # pragma nocover
def exists(self, service: str) -> bool: def exists(self, service: str) -> bool:
... ... # pragma nocover

View file

@ -2,13 +2,14 @@ import random
import string import string
from typing import Protocol from typing import Protocol
from pydantic import BaseModel from pydantic.dataclasses import dataclass
from app.models.password import Password from app.models.password import Password
from app.repositories.type import Repository from app.repositories.type import Repository
class PassGenOptions(BaseModel): @dataclass(frozen=True)
class PassGenOptions:
service: str service: str
seed: int seed: int
length: int = 8 length: int = 8

View file

@ -11,3 +11,8 @@ warn_unused_ignores = True
warn_unreachable = True warn_unreachable = True
warn_redundant_casts = True warn_redundant_casts = True
disallow_untyped_defs=True disallow_untyped_defs=True
plugins = pydantic.mypy
[pydantic-mypy]
init_forbid_extra = True
init_typed = True

View file

@ -1,10 +1,9 @@
from typing import Any from typing import Any
from faker import Factory from faker import Factory
from app.main import app
from typer.testing import CliRunner, Result from typer.testing import CliRunner, Result
from app.main import app
runner = CliRunner() runner = CliRunner()
faker = Factory.create() faker = Factory.create()
@ -49,6 +48,13 @@ def test_cli_can_save_to_db() -> None:
assert "2yW4AcqG" in result.stdout assert "2yW4AcqG" in result.stdout
def test_cli_cannot_save_password_for_service_twice() -> None:
service_name = faker.pystr()
runner.invoke(app, ["save", service_name, "--no-random"])
result = runner.invoke(app, ["save", service_name, "--no-random"])
assert "already been set" in result.stdout
def _run_cli(*args: Any) -> Result: def _run_cli(*args: Any) -> Result:
result = runner.invoke(app, ["save", faker.pystr(), "--no-random", *args]) result = runner.invoke(app, ["save", faker.pystr(), "--no-random", *args])
assert result.exit_code == 0 assert result.exit_code == 0

View file

@ -1,12 +1,11 @@
import pytest import pytest
from pydantic import SecretStr from faker import Factory
from app.models.password import Password
from app.repositories.fake import FakeRepository from app.repositories.fake import FakeRepository
from app.usecases.pass_gen import PassGenOptions, generate_password, list_all_saved_passwords from app.usecases.pass_gen import PassGenOptions, generate_password, list_all_saved_passwords
fake_repo = FakeRepository.get_instance() fake_repo = FakeRepository.get_instance()
service = "service_name" faker = Factory.create()
@pytest.mark.parametrize( @pytest.mark.parametrize(
@ -17,7 +16,7 @@ service = "service_name"
], ],
) )
def test_can_generate_random_password(seed: int, expected: str) -> None: def test_can_generate_random_password(seed: int, expected: str) -> None:
options = PassGenOptions(seed=seed, service=service) options = PassGenOptions(seed=seed, service=faker.pystr())
assert generate_password(fake_repo, options) == expected assert generate_password(fake_repo, options) == expected
@ -29,7 +28,7 @@ def test_can_generate_random_password(seed: int, expected: str) -> None:
], ],
) )
def test_control_password_length(seed: int, length: int, expected: str) -> None: def test_control_password_length(seed: int, length: int, expected: str) -> None:
options = PassGenOptions(seed=seed, length=length, service=service) options = PassGenOptions(seed=seed, length=length, service=faker.pystr())
assert generate_password(fake_repo, options) == expected assert generate_password(fake_repo, options) == expected
@ -41,7 +40,7 @@ def test_control_password_length(seed: int, length: int, expected: str) -> None:
], ],
) )
def test_password_can_contain_symbols(seed: int, symbols: bool, expected: str) -> None: def test_password_can_contain_symbols(seed: int, symbols: bool, expected: str) -> None:
options = PassGenOptions(seed=seed, symbols=symbols, service=service) options = PassGenOptions(seed=seed, symbols=symbols, service=faker.pystr())
assert generate_password(fake_repo, options) == expected assert generate_password(fake_repo, options) == expected
@ -53,18 +52,17 @@ def test_password_can_contain_symbols(seed: int, symbols: bool, expected: str) -
], ],
) )
def test_password_can_contain_numbers(seed: int, numbers: bool, expected: str) -> None: def test_password_can_contain_numbers(seed: int, numbers: bool, expected: str) -> None:
options = PassGenOptions(seed=seed, numbers=numbers, service=service) options = PassGenOptions(seed=seed, numbers=numbers, service=faker.pystr())
assert generate_password(fake_repo, options) == expected assert generate_password(fake_repo, options) == expected
@pytest.mark.parametrize( @pytest.mark.parametrize("expected", [8])
"expected", def test_can_read_all_saved_passwords(expected: int) -> None:
[ assert len(list_all_saved_passwords(fake_repo)) == expected
[
Password(id=0, service="first", password=SecretStr("2yW4AcqG")),
Password(id=1, service="second", password=SecretStr("iK2ZWeqh")), def test_cannot_save_password_for_service_twice() -> None:
], with pytest.raises(ValueError, match="already been set"):
], options = PassGenOptions(seed=0, service=faker.pystr())
) generate_password(fake_repo, options)
def test_can_read_all_saved_passwords(expected: list[str]) -> None: generate_password(fake_repo, options)
assert list_all_saved_passwords(fake_repo) == expected