mirror of
https://github.com/rjNemo/pass-gen
synced 2026-06-06 02:26:42 +00:00
refactor: fake repo and tesrt coverage
This commit is contained in:
parent
183f739223
commit
6332b9e233
10 changed files with 52 additions and 35 deletions
8
.coveragerc
Normal file
8
.coveragerc
Normal file
|
|
@ -0,0 +1,8 @@
|
||||||
|
[run]
|
||||||
|
omit =
|
||||||
|
# omit tests
|
||||||
|
*/tests/*
|
||||||
|
# omit non testable files
|
||||||
|
app/__main__.py
|
||||||
|
[html]
|
||||||
|
directory = .htmlcov
|
||||||
2
.gitignore
vendored
2
.gitignore
vendored
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
from .main import app
|
from .main import app
|
||||||
|
|
||||||
|
|
||||||
app() # pragma nocover
|
app()
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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:
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue