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
pip-log.txt
pip-delete-this-directory.txt
htmlcov/
.htmlcov/
.tox/
.nox/
.coverage

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -11,3 +11,8 @@ warn_unused_ignores = True
warn_unreachable = True
warn_redundant_casts = 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 faker import Factory
from app.main import app
from typer.testing import CliRunner, Result
from app.main import app
runner = CliRunner()
faker = Factory.create()
@ -49,6 +48,13 @@ def test_cli_can_save_to_db() -> None:
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:
result = runner.invoke(app, ["save", faker.pystr(), "--no-random", *args])
assert result.exit_code == 0

View file

@ -1,12 +1,11 @@
import pytest
from pydantic import SecretStr
from faker import Factory
from app.models.password import Password
from app.repositories.fake import FakeRepository
from app.usecases.pass_gen import PassGenOptions, generate_password, list_all_saved_passwords
fake_repo = FakeRepository.get_instance()
service = "service_name"
faker = Factory.create()
@pytest.mark.parametrize(
@ -17,7 +16,7 @@ service = "service_name"
],
)
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
@ -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:
options = PassGenOptions(seed=seed, length=length, service=service)
options = PassGenOptions(seed=seed, length=length, service=faker.pystr())
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:
options = PassGenOptions(seed=seed, symbols=symbols, service=service)
options = PassGenOptions(seed=seed, symbols=symbols, service=faker.pystr())
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:
options = PassGenOptions(seed=seed, numbers=numbers, service=service)
options = PassGenOptions(seed=seed, numbers=numbers, service=faker.pystr())
assert generate_password(fake_repo, options) == expected
@pytest.mark.parametrize(
"expected",
[
[
Password(id=0, service="first", password=SecretStr("2yW4AcqG")),
Password(id=1, service="second", password=SecretStr("iK2ZWeqh")),
],
],
)
def test_can_read_all_saved_passwords(expected: list[str]) -> None:
assert list_all_saved_passwords(fake_repo) == expected
@pytest.mark.parametrize("expected", [8])
def test_can_read_all_saved_passwords(expected: int) -> None:
assert len(list_all_saved_passwords(fake_repo)) == expected
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)
generate_password(fake_repo, options)