From 692d592daa3f7dd1f8c6aae477203570fe75a0fa Mon Sep 17 00:00:00 2001 From: leeroymk Date: Sat, 30 Mar 2024 10:19:36 +0300 Subject: [PATCH 1/4] first test done, pytest, ruff configs added --- .gitignore | 4 ++ pytest.ini | 3 +- ruff.toml | 75 ++++++++++++++++++++++++++++++ tests/level_4/test_one_brackets.py | 30 ++++++++++++ 4 files changed, 111 insertions(+), 1 deletion(-) create mode 100644 .gitignore create mode 100644 ruff.toml create mode 100644 tests/level_4/test_one_brackets.py diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..2647a7d2 --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +.venv/ +__pycache__/ +.coverage +.pytest_cache/ diff --git a/pytest.ini b/pytest.ini index 9c9afad3..a89f8922 100644 --- a/pytest.ini +++ b/pytest.ini @@ -1,2 +1,3 @@ [pytest] -addopts = --cov=. --cov-branch --testdox --cov-fail-under=60 -vv +; addopts = --cov=. --cov-branch --testdox --cov-fail-under=60 -vv +addopts = --testdox -vv diff --git a/ruff.toml b/ruff.toml new file mode 100644 index 00000000..f88c06b0 --- /dev/null +++ b/ruff.toml @@ -0,0 +1,75 @@ +# Exclude a variety of commonly ignored directories. +exclude = [ + ".bzr", + ".direnv", + ".eggs", + ".git", + ".git-rewrite", + ".hg", + ".ipynb_checkpoints", + ".mypy_cache", + ".nox", + ".pants.d", + ".pyenv", + ".pytest_cache", + ".pytype", + ".ruff_cache", + ".svn", + ".tox", + ".venv", + ".vscode", + "__pypackages__", + "_build", + "buck-out", + "build", + "dist", + "node_modules", + "site-packages", + "venv", + "functions" +] + +# Same as Black. +line-length = 88 +indent-width = 4 + +[lint] +# Enable Pyflakes (`F`) and a subset of the pycodestyle (`E`) codes by default. +# Unlike Flake8, Ruff doesn't enable pycodestyle warnings (`W`) or +# McCabe complexity (`C901`) by default. +select = ["E4", "E7", "E9", "F"] +ignore = [] + +# Allow fix for all enabled rules (when `--fix`) is provided. +fixable = ["ALL"] +unfixable = [] + +# Allow unused variables when underscore-prefixed. +dummy-variable-rgx = "^(_+|(_+[a-zA-Z0-9_]*[a-zA-Z0-9]+?))$" + +[format] +# Like Black, use double quotes for strings. +quote-style = "double" + +# Like Black, indent with spaces, rather than tabs. +indent-style = "space" + +# Like Black, respect magic trailing commas. +skip-magic-trailing-comma = false + +# Like Black, automatically detect the appropriate line ending. +line-ending = "auto" + +# Enable auto-formatting of code examples in docstrings. Markdown, +# reStructuredText code/literal blocks and doctests are all supported. +# +# This is currently disabled by default, but it is planned for this +# to be opt-out in the future. +docstring-code-format = false + +# Set the line length limit used when formatting code snippets in +# docstrings. +# +# This only has an effect when the `docstring-code-format` setting is +# enabled. +docstring-code-line-length = "dynamic" diff --git a/tests/level_4/test_one_brackets.py b/tests/level_4/test_one_brackets.py new file mode 100644 index 00000000..0f265449 --- /dev/null +++ b/tests/level_4/test_one_brackets.py @@ -0,0 +1,30 @@ +import pytest +from functions.level_4.one_brackets import delete_remove_brackets_quotes + + +@pytest.mark.parametrize("name", ["text", "not_text", "kinda_text"]) +def test__delete_remove_brackets_quotes__cut_correctly_if_in_brackets_and_spaces( + name, +): + assert delete_remove_brackets_quotes("{ " + name + " }") == name + + +@pytest.mark.xpass(reason="is the right behavior?") +def test__delete_remove_brackets_quotes__cut_incorrectly_if_in_brackets_no_spaces(): + assert delete_remove_brackets_quotes("{" + "text" + "}") == "ex" + + +@pytest.mark.parametrize("name", ["t", "te", "tex"]) +def test__delete_remove_brackets_quotes__return_name_if_no_brackets(name): + assert delete_remove_brackets_quotes(name) == name + + +def test__delete_remove_brackets_quotes__raises_exception_if_name_is_empty(): + with pytest.raises(IndexError): + delete_remove_brackets_quotes("") + + +@pytest.mark.parametrize( + "name", ["{}", "{ }", "{ }"]) +def test_delete_remove_brackets_quotes__return_empty_string_if_name_is_empty(name): + assert delete_remove_brackets_quotes(name) == "" \ No newline at end of file From 8cbf0f086ac45881e05dcf655479b9b07d426f6f Mon Sep 17 00:00:00 2001 From: leeroymk Date: Sat, 30 Mar 2024 13:32:45 +0300 Subject: [PATCH 2/4] second tests done, pre-commit added --- .pre-commit-config.yaml | 7 +++++ requirements.txt | 14 ++++++++++ tests/level_4/conftest.py | 44 ++++++++++++++++++++++++++++++ tests/level_4/test_one_brackets.py | 5 ++-- tests/level_4/test_two_students.py | 29 ++++++++++++++++++++ 5 files changed, 96 insertions(+), 3 deletions(-) create mode 100644 .pre-commit-config.yaml create mode 100644 tests/level_4/conftest.py create mode 100644 tests/level_4/test_two_students.py diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 00000000..16522b87 --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,7 @@ +repos: + - repo: https://github.com/astral-sh/ruff-pre-commit + rev: v0.3.4 + hooks: + - id: ruff + args: [ --fix ] + - id: ruff-format diff --git a/requirements.txt b/requirements.txt index 68abc7a6..a00f54f2 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,16 +1,30 @@ +asarPy==1.0.1 attrs==23.2.0 +cfgv==3.4.0 coverage==7.4.3 +distlib==0.3.8 exceptiongroup==1.2.0 +Faker==24.4.0 +filelock==3.13.3 +identify==2.5.35 iniconfig==2.0.0 markdown-it-py==3.0.0 mdurl==0.1.2 +nodeenv==1.8.0 packaging==23.2 +platformdirs==4.2.0 pluggy==1.4.0 pprintpp==0.4.0 +pre-commit==3.7.0 Pygments==2.17.2 pytest==7.2.1 pytest-clarity==1.0.1 pytest-cov==4.1.0 pytest-testdox==3.1.0 +python-dateutil==2.9.0.post0 +PyYAML==6.0.1 rich==13.7.1 +ruff==0.3.4 +six==1.16.0 tomli==2.0.1 +virtualenv==20.25.1 diff --git a/tests/level_4/conftest.py b/tests/level_4/conftest.py new file mode 100644 index 00000000..81aab16b --- /dev/null +++ b/tests/level_4/conftest.py @@ -0,0 +1,44 @@ +import typing as t + +from faker import Faker +import pytest + +from functions.level_4.two_students import Student + + +NOT_SET: t.Any = "____" + +faker = Faker() + + +@pytest.fixture +def make_student(faker): + def inner( + first_name: str = NOT_SET, + last_name: str = NOT_SET, + telegram_account: str | None = NOT_SET, + ): + first_name = faker.first_name() if first_name == NOT_SET else first_name + last_name = faker.last_name() if last_name == NOT_SET else last_name + telegram_account = ( + "@" + faker.simple_profile()["username"] + if telegram_account == NOT_SET + else telegram_account + ) + return Student( + first_name=first_name, + last_name=last_name, + telegram_account=telegram_account, + ) + + return inner + + +# @pytest.fixture +# def students(): +# return [ +# make_student(telegram_account="alfa"), +# make_student(telegram_account="beta"), +# make_student(telegram_account="gamma"), +# make_student(telegram_account=None), +# ] diff --git a/tests/level_4/test_one_brackets.py b/tests/level_4/test_one_brackets.py index 0f265449..d4787c1f 100644 --- a/tests/level_4/test_one_brackets.py +++ b/tests/level_4/test_one_brackets.py @@ -24,7 +24,6 @@ def test__delete_remove_brackets_quotes__raises_exception_if_name_is_empty(): delete_remove_brackets_quotes("") -@pytest.mark.parametrize( - "name", ["{}", "{ }", "{ }"]) +@pytest.mark.parametrize("name", ["{}", "{ }", "{ }"]) def test_delete_remove_brackets_quotes__return_empty_string_if_name_is_empty(name): - assert delete_remove_brackets_quotes(name) == "" \ No newline at end of file + assert delete_remove_brackets_quotes(name) == "" diff --git a/tests/level_4/test_two_students.py b/tests/level_4/test_two_students.py new file mode 100644 index 00000000..75c159e8 --- /dev/null +++ b/tests/level_4/test_two_students.py @@ -0,0 +1,29 @@ +from functions.level_4.two_students import get_student_by_tg_nickname + + +def test__get_student_by_tg_nickname__return_student_if_nickname_in_list(make_student): + students = [ + make_student(telegram_account="alfa"), + make_student(telegram_account="beta"), + make_student(telegram_account="gamma"), + make_student(telegram_account=None), + ] + + assert get_student_by_tg_nickname("alfa", students) == students[0] + + +def test__get_student_by_tg_nickname__return_none_if_no_such_nickname(make_student): + students = [ + make_student(telegram_account="alfa"), + make_student(telegram_account="beta"), + make_student(telegram_account="gamma"), + make_student(telegram_account=None), + ] + + assert get_student_by_tg_nickname("epsilon", students) is None + + +def test__get_student_by_tg_nickname__return_none_if_no_students(): + students = [] + + assert get_student_by_tg_nickname("alfa", students) is None From 6b93bd6f669d358b4d6858a1e4b9f89eb9ad2b48 Mon Sep 17 00:00:00 2001 From: leeroymk Date: Sat, 30 Mar 2024 14:20:08 +0300 Subject: [PATCH 3/4] format --- tests/level_4/conftest.py | 10 -------- tests/level_4/test_three_promocodes.py | 32 ++++++++++++++++++++++++++ 2 files changed, 32 insertions(+), 10 deletions(-) create mode 100644 tests/level_4/test_three_promocodes.py diff --git a/tests/level_4/conftest.py b/tests/level_4/conftest.py index 81aab16b..1c5188da 100644 --- a/tests/level_4/conftest.py +++ b/tests/level_4/conftest.py @@ -32,13 +32,3 @@ def inner( ) return inner - - -# @pytest.fixture -# def students(): -# return [ -# make_student(telegram_account="alfa"), -# make_student(telegram_account="beta"), -# make_student(telegram_account="gamma"), -# make_student(telegram_account=None), -# ] diff --git a/tests/level_4/test_three_promocodes.py b/tests/level_4/test_three_promocodes.py new file mode 100644 index 00000000..96200a76 --- /dev/null +++ b/tests/level_4/test_three_promocodes.py @@ -0,0 +1,32 @@ +import pytest +from functions.level_4.three_promocodes import generate_promocode + + +@pytest.mark.parametrize("promocode_len", [-1, -10, -100]) +def test__generate_promocode__no_promocode_if_promocode_len_is_negative(promocode_len): + assert len(generate_promocode(promocode_len)) == 0 + + +@pytest.mark.parametrize("promocode_len", [1, 10, 100]) +def test__generate_promocode__return_promocode(promocode_len): + assert len(generate_promocode(promocode_len)) == promocode_len + + +def test__generate_promocode__promocode_len_is_8_if_no_argument(): + assert len(generate_promocode()) == 8 + + +def test__generate_promocode__no_promocode_if_promocode_len_is_0(): + assert len(generate_promocode(0)) == 0 + + +def test__generate_promocode__only_uppercase_letters(): + assert (generate_promocode(500)).isupper() + + +def test__generate_promocode__only_letters(): + assert (generate_promocode(500)).isalpha() + + +def test__generate_promocode__return_str(): + assert isinstance(generate_promocode(10), str) From f35f041cb22f2fa831ff0b694350fda8bcde6ce4 Mon Sep 17 00:00:00 2001 From: leeroymk Date: Mon, 1 Apr 2024 22:11:06 +0300 Subject: [PATCH 4/4] fix issues --- .gitignore | 2 + tests/level_1/test_four_bank_parser.py | 4 +- tests/level_1/test_two_date_parser.py | 4 +- tests/level_2/test_five_replace_word.py | 12 +-- tests/level_2/test_four_sentiment.py | 4 +- tests/level_2/test_two_square_equation.py | 22 ++---- tests/level_3/conftest.py | 49 +++++++----- tests/level_3/test_four_fraud.py | 74 ++++++------------- tests/level_3/test_one_avg_daily_expenses.py | 21 +++--- tests/level_3/test_three_is_subscription.py | 50 +++++++++---- tests/level_3/test_two_expense_categorizer.py | 9 +-- tests/level_4/conftest.py | 34 --------- tests/level_4/test_one_brackets.py | 29 -------- tests/level_4/test_three_promocodes.py | 32 -------- tests/level_4/test_two_students.py | 29 -------- 15 files changed, 114 insertions(+), 261 deletions(-) delete mode 100644 tests/level_4/conftest.py delete mode 100644 tests/level_4/test_one_brackets.py delete mode 100644 tests/level_4/test_three_promocodes.py delete mode 100644 tests/level_4/test_two_students.py diff --git a/.gitignore b/.gitignore index 2647a7d2..90208fab 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,5 @@ __pycache__/ .coverage .pytest_cache/ +.vscode +.ruff_cache diff --git a/tests/level_1/test_four_bank_parser.py b/tests/level_1/test_four_bank_parser.py index 5c8046d1..a1271333 100644 --- a/tests/level_1/test_four_bank_parser.py +++ b/tests/level_1/test_four_bank_parser.py @@ -25,9 +25,7 @@ def test__parse_ineco_expense__return_correct_amount(sms, amount, make_sms, card ("200 200.5, 0123 13.03.24 16:36 12:01 shop", "2024-03-13T16:36:00"), ], ) -def test__parse_ineco_expense__return_correct_spent_at( - sms, expected_spent_at, make_sms, cards -): +def test__parse_ineco_expense__return_correct_spent_at(sms, expected_spent_at, make_sms, cards): sms_message = make_sms(text=sms) expense = parse_ineco_expense(sms_message, cards) assert expense.spent_at.isoformat() == expected_spent_at diff --git a/tests/level_1/test_two_date_parser.py b/tests/level_1/test_two_date_parser.py index 8da1703f..df952037 100644 --- a/tests/level_1/test_two_date_parser.py +++ b/tests/level_1/test_two_date_parser.py @@ -18,9 +18,7 @@ def test__compose_datetime_from__any_time_tomorrow_returns_tomorrow(time_str, to assert compose_datetime_from("tomorrow", time_str).date() == tomorrow -@pytest.mark.parametrize( - "time_str, expected", [("12:00", 12), ("00:00", 0), ("23:59", 23)] -) +@pytest.mark.parametrize("time_str, expected", [("12:00", 12), ("00:00", 0), ("23:59", 23)]) def test__compose_datetime_from__hour_parsing_valid(time_str, expected): assert compose_datetime_from("tomorrow", time_str).hour == expected diff --git a/tests/level_2/test_five_replace_word.py b/tests/level_2/test_five_replace_word.py index 0a15482f..3a6b98d1 100644 --- a/tests/level_2/test_five_replace_word.py +++ b/tests/level_2/test_five_replace_word.py @@ -4,15 +4,11 @@ def test__replace_word__replace_from_in_text_return_valid_text(): - assert ( - replace_word("some words to replace", "some", "any") == "any words to replace" - ) + assert replace_word("some words to replace", "some", "any") == "any words to replace" def test__replace_word__replace_from_not_in_text_return_old_text(): - assert ( - replace_word("some words to replace", "bad", "any") == "some words to replace" - ) + assert replace_word("some words to replace", "bad", "any") == "some words to replace" @pytest.mark.parametrize( @@ -23,7 +19,5 @@ def test__replace_word__replace_from_not_in_text_return_old_text(): ("SOMe words to replace", "some", "any", "any words to replace"), ], ) -def test__replace_word__ignore_case_return_valid_text( - text, replace_from, replace_to, expected -): +def test__replace_word__ignore_case_return_valid_text(text, replace_from, replace_to, expected): assert replace_word(text, replace_from, replace_to) == expected diff --git a/tests/level_2/test_four_sentiment.py b/tests/level_2/test_four_sentiment.py index df06b9e1..3769c587 100644 --- a/tests/level_2/test_four_sentiment.py +++ b/tests/level_2/test_four_sentiment.py @@ -24,9 +24,7 @@ def test__check_tweet_sentiment__bad_and_good_words_are_equal_return_None( "You're not good enough", ], ) -def test__check_tweet_sentiment__more_good_words_return_GOOD( - text, good_words, bad_words -): +def test__check_tweet_sentiment__more_good_words_return_GOOD(text, good_words, bad_words): assert check_tweet_sentiment(text, good_words, bad_words) == "GOOD" diff --git a/tests/level_2/test_two_square_equation.py b/tests/level_2/test_two_square_equation.py index 9cccd80d..fa3665f2 100644 --- a/tests/level_2/test_two_square_equation.py +++ b/tests/level_2/test_two_square_equation.py @@ -6,19 +6,11 @@ @pytest.mark.parametrize( "a, b, c", [ - ( - 90.2, - 8.3, - 1.4, - ), - ( - 55.5, - 0.0, - 2.2, - ), + (90.2, 8.3, 1.4), + (55.5, 0.0, 2.2), ], ) -def test__solve_square_equation__discriminant_lt_zero_return_no_roots(a, b, c): +def test__solve_square_equation__return_no_roots_if_discriminant_lt_zero(a, b, c): assert solve_square_equation(a, b, c) == (None, None) @@ -29,7 +21,9 @@ def test__solve_square_equation__discriminant_lt_zero_return_no_roots(a, b, c): (2.5, 1.0, (-0.4, None)), ], ) -def test__solve_square_equation__square_coef_zero_linear_coef_not_zero_return_one_root(b, c, root): +def test__solve_square_equation__return_one_root_with_zeroes_in_square_and_linear_coefficients( + b, c, root +): assert solve_square_equation(0.0, b, c) == root @@ -37,7 +31,7 @@ def test__solve_square_equation__square_coef_zero_linear_coef_not_zero_return_on "c", [1.0, -57.2, 0.0], ) -def test__solve_square_equation__square_coef_zero_linear_coef_zero_return_no_roots(c): +def test__solve_square_equation__return_no_roots__square_coef_zero_linear_coef_zero(c): assert solve_square_equation(0.0, 0.0, c) == (None, None) @@ -48,7 +42,7 @@ def test__solve_square_equation__square_coef_zero_linear_coef_zero_return_no_roo (2.0, 4.0, 0.0, (-2.0, 0.0)), ], ) -def test__solve_square_equation__square_coef_not_zero_linear_coef_not_zero_return_two_numbers( +def test__solve_square_equation__return_two_numbers_if_square_coef_not_zero_linear_coef_not_zero( a, b, c, roots ): assert solve_square_equation(a, b, c) == roots diff --git a/tests/level_3/conftest.py b/tests/level_3/conftest.py index d4a846c4..d225d6d3 100644 --- a/tests/level_3/conftest.py +++ b/tests/level_3/conftest.py @@ -3,13 +3,34 @@ import typing as t import pytest -from faker import Faker from functions.level_3.models import BankCard, Expense, ExpenseCategory NOT_SET: t.Any = "____" -faker = Faker() + +def defval(value: t.Any, default: t.Any) -> t.Any: + return default if value is NOT_SET else value + + +@pytest.fixture +def make_card(faker): + return BankCard(last_digits=faker.credit_card_number()[:-4], owner=faker.name()) + + +@pytest.fixture +def make_spent_at(faker): + return faker.past_date() + + +@pytest.fixture +def make_spent_in(faker): + return faker.company() + + +@pytest.fixture +def make_amount_lt_five_thousand(faker): + return faker.pyfloat(max_value=5000) @pytest.fixture @@ -22,24 +43,12 @@ def inner( spent_at: datetime.datetime = NOT_SET, category: ExpenseCategory = NOT_SET, ): - amount = ( - faker.pyfloat(left_digits=3, right_digits=2, positive=True) - if amount is NOT_SET - else amount - ) - currency = faker.currency_code() if currency is NOT_SET else currency - card = ( - BankCard(last_digits=faker.credit_card_number()[-4:], owner=faker.name()) - if card is NOT_SET - else card - ) - spent_in = faker.company() if spent_in is NOT_SET else spent_in - spent_at = faker.date_time_this_year() if spent_at is NOT_SET else spent_at - category = ( - faker.random_element(elements=ExpenseCategory) - if category is NOT_SET - else category - ) + amount = defval(amount, faker.pyfloat(left_digits=3, right_digits=2, positive=True)) + currency = defval(currency, faker.currency_code()) + card = defval(card, make_card) + spent_in = defval(spent_in, make_spent_in) + spent_at = defval(spent_at, faker.past_date()) + category = defval(category, faker.enum(ExpenseCategory)) return Expense( amount=decimal.Decimal(str(amount)), diff --git a/tests/level_3/test_four_fraud.py b/tests/level_3/test_four_fraud.py index 5cf46f58..26da392e 100644 --- a/tests/level_3/test_four_fraud.py +++ b/tests/level_3/test_four_fraud.py @@ -5,90 +5,64 @@ from functions.level_3.four_fraud import find_fraud_expenses -def test__find_fraud_expenses__no_fraud_empty_history(): +def test__find_fraud_expenses__return_empty_list_for_empty_history(): assert find_fraud_expenses([]) == [] +# parametrize or fixture is better? @pytest.mark.parametrize("amount", [3000, 5000]) -def test__find_fraud_expenses__fraud_found_over_3_payments_less_or_equal_5000_at_one_time( - make_expense, amount +def test__find_fraud_expenses__find_fraud_gt_three_payments_lt_or_eq_five_thousand_in_one_time_and_place( + make_expense, amount, make_spent_at, make_spent_in ): expenses = [ - make_expense( - spent_in="chinar", - spent_at=datetime.datetime(2024, 3, 1), - amount=amount, - ), - make_expense( - spent_in="chinar", - spent_at=datetime.datetime(2024, 3, 1), - amount=amount, - ), - make_expense( - spent_in="chinar", - spent_at=datetime.datetime(2024, 3, 1), - amount=amount, - ), + make_expense(spent_in=make_spent_in, spent_at=make_spent_at, amount=amount), + make_expense(spent_in=make_spent_in, spent_at=make_spent_at, amount=amount), + make_expense(spent_in=make_spent_in, spent_at=make_spent_at, amount=amount), ] assert find_fraud_expenses(expenses) == expenses -def test__find_fraud_expenses__no_fraud_found_over_3_payments_over_5000_at_one_time( - make_expense, +@pytest.mark.parametrize("amount", [6000, 100000]) +def test__find_fraud_expenses__find_no_fraud_gt_three_payments_over_five_thousand_in_one_time_and_place( + make_expense, make_spent_at, make_spent_in, amount ): expenses = [ - make_expense( - spent_in="chinar", - spent_at=datetime.datetime(2024, 3, 1), - amount=6000, - ), - make_expense( - spent_in="chinar", - spent_at=datetime.datetime(2024, 3, 1), - amount=6000, - ), - make_expense( - spent_in="chinar", - spent_at=datetime.datetime(2024, 3, 1), - amount=6000, - ), + make_expense(spent_in=make_spent_in, spent_at=make_spent_at, amount=amount), + make_expense(spent_in=make_spent_in, spent_at=make_spent_at, amount=amount), + make_expense(spent_in=make_spent_in, spent_at=make_spent_at, amount=amount), ] assert find_fraud_expenses(expenses) == [] -def test__find_fraud_expenses__no_fraud_found_2_equal_purchases_below_max_amount_at_one_day( - make_expense, +@pytest.mark.parametrize("amount", [4999, 1, 500]) +def test__find_fraud_expenses__find_no_fraud_for_lt_three_expenses( + make_expense, amount, make_spent_at ): expenses = [ - make_expense( - amount=500, - spent_at=datetime.datetime(2024, 3, 1), - ), - make_expense( - amount=500, - spent_at=datetime.datetime(2024, 3, 1), - ), + make_expense(amount=amount, spent_at=make_spent_at), + make_expense(amount=amount, spent_at=make_spent_at), ] assert find_fraud_expenses(expenses) == [] -def test__find_fraud_expenses__no_fraud_found_3_purchases_below_max_amount_at_different_days( - make_expense, +# parametrize or fixture is better? +def test__find_fraud_expenses__find_no_fraud_for_different_days_expenses( + make_expense, make_amount_lt_five_thousand ): expenses = [ make_expense( - amount=500, + amount=make_amount_lt_five_thousand, spent_at=datetime.datetime(2024, 3, 1), ), make_expense( - amount=500, + amount=make_amount_lt_five_thousand, spent_at=datetime.datetime(2024, 3, 2), ), make_expense( - amount=500, + amount=make_amount_lt_five_thousand, spent_at=datetime.datetime(2024, 3, 3), ), ] diff --git a/tests/level_3/test_one_avg_daily_expenses.py b/tests/level_3/test_one_avg_daily_expenses.py index 581b462e..e7874978 100644 --- a/tests/level_3/test_one_avg_daily_expenses.py +++ b/tests/level_3/test_one_avg_daily_expenses.py @@ -1,26 +1,23 @@ import datetime import decimal -from statistics import ( - StatisticsError, -) +from statistics import StatisticsError import pytest -from functions.level_3.one_avg_daily_expenses import ( - calculate_average_daily_expenses, -) +from functions.level_3.one_avg_daily_expenses import calculate_average_daily_expenses -def test__calculate_average_daily_expenses__correct_average_when_spent_in_different_days( - make_expense, +@pytest.mark.parametrize("amount", [1, 1000, 100000]) +def test__calculate_average_daily_expenses__return_correct_average_when_spent_in_different_days( + make_expense, amount ): expenses = [ - make_expense(amount=100, spent_at=datetime.datetime(2024, 3, 1)), - make_expense(amount=100, spent_at=datetime.datetime(2024, 4, 1)), - make_expense(amount=100, spent_at=datetime.datetime(2024, 5, 1)), + make_expense(amount=amount, spent_at=datetime.datetime(2024, 3, 1)), + make_expense(amount=amount, spent_at=datetime.datetime(2024, 4, 1)), + make_expense(amount=amount, spent_at=datetime.datetime(2024, 5, 1)), ] - assert calculate_average_daily_expenses(expenses) == decimal.Decimal(100) + assert calculate_average_daily_expenses(expenses) == decimal.Decimal(amount) def test__calculate_average_daily_expenses__correct_average_when_spent_in_one_day( diff --git a/tests/level_3/test_three_is_subscription.py b/tests/level_3/test_three_is_subscription.py index e9eee165..4e4b8d13 100644 --- a/tests/level_3/test_three_is_subscription.py +++ b/tests/level_3/test_three_is_subscription.py @@ -1,49 +1,67 @@ import datetime +import pytest + from functions.level_3.three_is_subscription import is_subscription -def test__is_subscription__is_subscription_3_plus_purchases_in_1_place_at_most_once_a_month( - make_expense, -): +def test__is_subscription__return_true_for_three_months_purchases(make_expense, make_spent_in): first_expense = make_expense( - spent_in="chinar", + spent_in=make_spent_in, spent_at=datetime.datetime(2024, 3, 1), ) second_expense = make_expense( - spent_in="chinar", + spent_in=make_spent_in, spent_at=datetime.datetime(2024, 4, 1), ) third_expense = make_expense( - spent_in="chinar", + spent_in=make_spent_in, spent_at=datetime.datetime(2024, 5, 1), ) assert is_subscription(first_expense, [first_expense, second_expense, third_expense]) is True -def test__is_subscription__is_not_subscription_3_plus_purchases_in_1_place_at_most_once_a_month( - make_expense, +def test__is_subscription__return_false_when_lt_three_months_purchases( + make_expense, make_spent_in ): first_expense = make_expense( - spent_in="chinar", + spent_in=make_spent_in, spent_at=datetime.datetime(2024, 3, 1), ) second_expense = make_expense( - spent_in="chinar", + spent_in=make_spent_in, spent_at=datetime.datetime(2024, 3, 2), ) third_expense = make_expense( - spent_in="chinar", + spent_in=make_spent_in, spent_at=datetime.datetime(2024, 4, 1), ) assert is_subscription(first_expense, [first_expense, second_expense, third_expense]) is False -def test__is_subscription__is_not_subscription_less_than_3_purchases_in_1_place( - make_expense, +@pytest.mark.xfail(reason="is the right behavior") +def test__is_subscription__return_false_when_has_month_without_payment( + make_expense, make_spent_in ): - first_expense = make_expense(spent_in="chinar") - second_expense = make_expense(spent_in="chinar") - assert is_subscription(first_expense, [first_expense, second_expense]) is False + first_expense = make_expense( + spent_in=make_spent_in, + spent_at=datetime.datetime(2023, 1, 1), + ) + second_expense = make_expense( + spent_in=make_spent_in, + spent_at=datetime.datetime(2023, 3, 1), + ) + third_expense = make_expense( + spent_in=make_spent_in, + spent_at=datetime.datetime(2023, 12, 1), + ) + assert is_subscription(first_expense, [first_expense, second_expense, third_expense]) is False + + +def test__is_subscription__return_false_when_payment_in_different_places(make_expense): + first_expense = make_expense(spent_in="marketplace") + second_expense = make_expense(spent_in="zoo") + third_expense = make_expense(spent_in="school") + assert is_subscription(first_expense, [first_expense, second_expense, third_expense]) is False diff --git a/tests/level_3/test_two_expense_categorizer.py b/tests/level_3/test_two_expense_categorizer.py index 309e3487..eb8fe39d 100644 --- a/tests/level_3/test_two_expense_categorizer.py +++ b/tests/level_3/test_two_expense_categorizer.py @@ -4,13 +4,8 @@ from functions.level_3.two_expense_categorizer import guess_expense_category -def test__guess_expense_category__return_none_if_no_spent_in( - make_expense, -): - expense = make_expense( - category=None, - spent_in="wildberries", - ) +def test__guess_expense_category__return_none_if_no_spent_in(make_expense): + expense = make_expense(category=None, spent_in="wildberries") assert guess_expense_category(expense) is None diff --git a/tests/level_4/conftest.py b/tests/level_4/conftest.py deleted file mode 100644 index 1c5188da..00000000 --- a/tests/level_4/conftest.py +++ /dev/null @@ -1,34 +0,0 @@ -import typing as t - -from faker import Faker -import pytest - -from functions.level_4.two_students import Student - - -NOT_SET: t.Any = "____" - -faker = Faker() - - -@pytest.fixture -def make_student(faker): - def inner( - first_name: str = NOT_SET, - last_name: str = NOT_SET, - telegram_account: str | None = NOT_SET, - ): - first_name = faker.first_name() if first_name == NOT_SET else first_name - last_name = faker.last_name() if last_name == NOT_SET else last_name - telegram_account = ( - "@" + faker.simple_profile()["username"] - if telegram_account == NOT_SET - else telegram_account - ) - return Student( - first_name=first_name, - last_name=last_name, - telegram_account=telegram_account, - ) - - return inner diff --git a/tests/level_4/test_one_brackets.py b/tests/level_4/test_one_brackets.py deleted file mode 100644 index d4787c1f..00000000 --- a/tests/level_4/test_one_brackets.py +++ /dev/null @@ -1,29 +0,0 @@ -import pytest -from functions.level_4.one_brackets import delete_remove_brackets_quotes - - -@pytest.mark.parametrize("name", ["text", "not_text", "kinda_text"]) -def test__delete_remove_brackets_quotes__cut_correctly_if_in_brackets_and_spaces( - name, -): - assert delete_remove_brackets_quotes("{ " + name + " }") == name - - -@pytest.mark.xpass(reason="is the right behavior?") -def test__delete_remove_brackets_quotes__cut_incorrectly_if_in_brackets_no_spaces(): - assert delete_remove_brackets_quotes("{" + "text" + "}") == "ex" - - -@pytest.mark.parametrize("name", ["t", "te", "tex"]) -def test__delete_remove_brackets_quotes__return_name_if_no_brackets(name): - assert delete_remove_brackets_quotes(name) == name - - -def test__delete_remove_brackets_quotes__raises_exception_if_name_is_empty(): - with pytest.raises(IndexError): - delete_remove_brackets_quotes("") - - -@pytest.mark.parametrize("name", ["{}", "{ }", "{ }"]) -def test_delete_remove_brackets_quotes__return_empty_string_if_name_is_empty(name): - assert delete_remove_brackets_quotes(name) == "" diff --git a/tests/level_4/test_three_promocodes.py b/tests/level_4/test_three_promocodes.py deleted file mode 100644 index 96200a76..00000000 --- a/tests/level_4/test_three_promocodes.py +++ /dev/null @@ -1,32 +0,0 @@ -import pytest -from functions.level_4.three_promocodes import generate_promocode - - -@pytest.mark.parametrize("promocode_len", [-1, -10, -100]) -def test__generate_promocode__no_promocode_if_promocode_len_is_negative(promocode_len): - assert len(generate_promocode(promocode_len)) == 0 - - -@pytest.mark.parametrize("promocode_len", [1, 10, 100]) -def test__generate_promocode__return_promocode(promocode_len): - assert len(generate_promocode(promocode_len)) == promocode_len - - -def test__generate_promocode__promocode_len_is_8_if_no_argument(): - assert len(generate_promocode()) == 8 - - -def test__generate_promocode__no_promocode_if_promocode_len_is_0(): - assert len(generate_promocode(0)) == 0 - - -def test__generate_promocode__only_uppercase_letters(): - assert (generate_promocode(500)).isupper() - - -def test__generate_promocode__only_letters(): - assert (generate_promocode(500)).isalpha() - - -def test__generate_promocode__return_str(): - assert isinstance(generate_promocode(10), str) diff --git a/tests/level_4/test_two_students.py b/tests/level_4/test_two_students.py deleted file mode 100644 index 75c159e8..00000000 --- a/tests/level_4/test_two_students.py +++ /dev/null @@ -1,29 +0,0 @@ -from functions.level_4.two_students import get_student_by_tg_nickname - - -def test__get_student_by_tg_nickname__return_student_if_nickname_in_list(make_student): - students = [ - make_student(telegram_account="alfa"), - make_student(telegram_account="beta"), - make_student(telegram_account="gamma"), - make_student(telegram_account=None), - ] - - assert get_student_by_tg_nickname("alfa", students) == students[0] - - -def test__get_student_by_tg_nickname__return_none_if_no_such_nickname(make_student): - students = [ - make_student(telegram_account="alfa"), - make_student(telegram_account="beta"), - make_student(telegram_account="gamma"), - make_student(telegram_account=None), - ] - - assert get_student_by_tg_nickname("epsilon", students) is None - - -def test__get_student_by_tg_nickname__return_none_if_no_students(): - students = [] - - assert get_student_by_tg_nickname("alfa", students) is None