From d1b1fd8e0b42fc21e36ace8cbdecb5de622d9c8d Mon Sep 17 00:00:00 2001 From: Ilia Miheev Date: Thu, 30 Apr 2026 00:41:27 +0300 Subject: [PATCH] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB=D0=B5?= =?UTF-8?q?=D0=BD=D1=8B=20=D1=82=D0=B5=D1=81=D1=82=D1=8B=20=D0=B4=D0=BB?= =?UTF-8?q?=D1=8F=20=D0=B2=D1=81=D0=B5=D1=85=20=D0=BA=D0=BB=D0=B0=D1=81?= =?UTF-8?q?=D1=81=D0=BE=D0=B2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tests/__init__.py | 0 tests/test_csv.py | 72 ++++++++++++++++ tests/test_json.py | 65 ++++++++++++++ tests/test_pars.py | 189 +++++++++++++++++++++++++++++++++++++++++ tests/test_progress.py | 29 +++++++ tests/test_timer.py | 69 +++++++++++++++ tests/test_zip.py | 87 +++++++++++++++++++ 7 files changed, 511 insertions(+) create mode 100644 tests/__init__.py create mode 100644 tests/test_csv.py create mode 100644 tests/test_json.py create mode 100644 tests/test_pars.py create mode 100644 tests/test_progress.py create mode 100644 tests/test_timer.py create mode 100644 tests/test_zip.py diff --git a/tests/__init__.py b/tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/test_csv.py b/tests/test_csv.py new file mode 100644 index 0000000..dd49314 --- /dev/null +++ b/tests/test_csv.py @@ -0,0 +1,72 @@ +import pytest +from ipars import CsvManager + + +def test_writerow_and_getrows(tmp_path): + cm = CsvManager() + filepath = str(tmp_path / 'data.csv') + row = ['Alice', '30', 'Engineer'] + + cm.writerow(filepath, 'w', row) + result = cm.getRows(filepath) + + assert result == [row] + + +def test_writerows_and_getrows(tmp_path): + cm = CsvManager() + filepath = str(tmp_path / 'data.csv') + rows = [['Alice', '30'], ['Bob', '25'], ['Carol', '28']] + + cm.writerows(filepath, 'w', rows) + result = cm.getRows(filepath) + + assert result == rows + + +def test_append_mode(tmp_path): + cm = CsvManager() + filepath = str(tmp_path / 'data.csv') + + cm.writerow(filepath, 'w', ['first']) + cm.writerow(filepath, 'a', ['second']) + result = cm.getRows(filepath) + + assert result == [['first'], ['second']] + + +def test_custom_delimiter(tmp_path): + cm = CsvManager(delimiter=',') + filepath = str(tmp_path / 'data.csv') + row = ['one', 'two', 'three'] + + cm.writerow(filepath, 'w', row) + result = cm.getRows(filepath) + + assert result == [row] + + +def test_invalid_mode_raises(tmp_path): + cm = CsvManager() + filepath = str(tmp_path / 'data.csv') + with pytest.raises(ValueError): + cm.writerow(filepath, 'x', ['data']) + + +def test_invalid_row_type_raises(tmp_path): + cm = CsvManager() + filepath = str(tmp_path / 'data.csv') + with pytest.raises(ValueError): + cm.writerow(filepath, 'w', 'not a list') + + +def test_invalid_constructor_args(): + with pytest.raises(ValueError): + CsvManager(delimiter=123) + + +def test_pprint_does_not_raise(capsys): + cm = CsvManager() + cm.pprint([['a', 'b'], ['c', 'd']]) + captured = capsys.readouterr() + assert 'a' in captured.out diff --git a/tests/test_json.py b/tests/test_json.py new file mode 100644 index 0000000..ef51a6e --- /dev/null +++ b/tests/test_json.py @@ -0,0 +1,65 @@ +import pytest +from ipars import JsonManager + + +def test_dump_and_load(tmp_path): + jm = JsonManager() + filepath = str(tmp_path / 'data.json') + data = {'name': 'ipars', 'version': 3, 'active': True} + + jm.dump(filepath, data) + result = jm.load(filepath) + + assert result == data + + +def test_dump_and_load_list(tmp_path): + jm = JsonManager() + filepath = str(tmp_path / 'list.json') + data = [1, 2, 3, 'hello'] + + jm.dump(filepath, data) + result = jm.load(filepath) + + assert result == data + + +def test_dump_and_load_with_cyrillic(tmp_path): + jm = JsonManager() + filepath = str(tmp_path / 'cyrillic.json') + data = {'текст': 'привет'} + + jm.dump(filepath, data) + result = jm.load(filepath) + + assert result == data + + +def test_load_nonexistent_file(): + jm = JsonManager() + with pytest.raises(FileNotFoundError): + jm.load('nonexistent_file_xyz.json') + + +def test_invalid_encoding_raises(): + with pytest.raises(ValueError): + JsonManager(encoding=123) + + +def test_load_invalid_path_type(): + jm = JsonManager() + with pytest.raises(ValueError): + jm.load(123) + + +def test_dump_invalid_path_type(tmp_path): + jm = JsonManager() + with pytest.raises(ValueError): + jm.dump(123, {'key': 'value'}) + + +def test_pprint_does_not_raise(capsys): + jm = JsonManager() + jm.pprint({'key': 'value'}) + captured = capsys.readouterr() + assert 'key' in captured.out diff --git a/tests/test_pars.py b/tests/test_pars.py new file mode 100644 index 0000000..4555d05 --- /dev/null +++ b/tests/test_pars.py @@ -0,0 +1,189 @@ +import pytest +from unittest.mock import patch, MagicMock +from bs4 import BeautifulSoup +from ipars import Pars + + +# --- exists --- + +def test_exists_true(tmp_path): + p = Pars() + assert p.exists(str(tmp_path)) is True + + +def test_exists_false(): + p = Pars() + assert p.exists('this_path_does_not_exist_xyz') is False + + +def test_exists_invalid_type(): + p = Pars() + with pytest.raises(ValueError): + p.exists(123) + + +# --- listdir --- + +def test_listdir_returns_list(tmp_path): + p = Pars() + (tmp_path / 'a.txt').write_text('a') + (tmp_path / 'b.txt').write_text('b') + result = p.listdir(str(tmp_path)) + assert isinstance(result, list) + assert 'a.txt' in result + assert 'b.txt' in result + + +def test_listdir_invalid_type(): + p = Pars() + with pytest.raises(ValueError): + p.listdir(42) + + +# --- mkdir --- + +def test_mkdir_creates_directory(tmp_path): + p = Pars() + new_dir = str(tmp_path / 'new_folder') + p.mkdir(new_dir) + assert (tmp_path / 'new_folder').exists() + + +def test_mkdir_does_not_raise_if_exists(tmp_path): + p = Pars() + existing = str(tmp_path) + p.mkdir(existing) # не должен бросать ошибку + + +def test_mkdir_invalid_type(): + p = Pars() + with pytest.raises(ValueError): + p.mkdir(99) + + +# --- returnBs4Object --- + +def test_returnBs4Object(tmp_path): + p = Pars() + html_file = tmp_path / 'page.html' + html_file.write_text('

Hello

', encoding='utf-8') + + soup = p.returnBs4Object(str(html_file)) + + assert soup.find('h1').text == 'Hello' + + +def test_returnBs4Object_returns_beautifulsoup(tmp_path): + p = Pars() + html_file = tmp_path / 'page.html' + html_file.write_text('

Test

', encoding='utf-8') + + result = p.returnBs4Object(str(html_file)) + + assert isinstance(result, BeautifulSoup) + + +# --- getTexts --- + +def _make_soup_elements(html: str) -> list: + soup = BeautifulSoup(html, 'lxml') + return soup.find_all('div') + + +def test_getTexts_basic(): + p = Pars() + elements = _make_soup_elements('
Hello
World
') + result = p.getTexts(elements) + assert result == ['Hello', 'World'] + + +def test_getTexts_needfix_strips_whitespace(): + p = Pars() + elements = _make_soup_elements('
Hello \n
\t World\t
') + result = p.getTexts(elements, needFix=True) + assert result == ['Hello', 'World'] + + +def test_getTexts_returns_none_when_empty(): + p = Pars() + elements = _make_soup_elements('
') + result = p.getTexts(elements) + assert result is None + + +def test_getTexts_invalid_type(): + p = Pars() + with pytest.raises(ValueError): + p.getTexts('not a list') + + +# --- getAttributes --- + +def test_getAttributes_basic(): + p = Pars() + soup = BeautifulSoup('12', 'lxml') + elements = soup.find_all('a') + result = p.getAttributes(elements, 'href') + assert result == ['http://a.com', 'http://b.com'] + + +def test_getAttributes_missing_attr(): + p = Pars() + soup = BeautifulSoup('
no href
', 'lxml') + elements = soup.find_all('div') + result = p.getAttributes(elements, 'href') + assert result is None + + +def test_getAttributes_invalid_type(): + p = Pars() + with pytest.raises(ValueError): + p.getAttributes('not a list', 'href') + + +# --- pprint --- + +def test_pprint_does_not_raise(capsys): + p = Pars() + p.pprint({'key': 'value', 'list': [1, 2, 3]}) + captured = capsys.readouterr() + assert 'key' in captured.out + + +# --- getStaticPage (с моком запроса) --- + +def test_getStaticPage_saves_file(tmp_path): + p = Pars() + filepath = str(tmp_path / 'page.html') + + mock_response = MagicMock() + mock_response.status_code = 200 + mock_response.text = 'Mocked' + + with patch('requests.get', return_value=mock_response): + status = p.getStaticPage(filepath, 'http://example.com') + + assert status == 200 + assert (tmp_path / 'page.html').read_text(encoding='utf-8') == mock_response.text + + +def test_getStaticPage_binary_mode(tmp_path): + p = Pars() + filepath = str(tmp_path / 'image.png') + + mock_response = MagicMock() + mock_response.status_code = 200 + mock_response.content = b'\x89PNG\r\n' + + with patch('requests.get', return_value=mock_response): + status = p.getStaticPage(filepath, 'http://example.com/img.png', writeMethod='wb') + + assert status == 200 + assert (tmp_path / 'image.png').read_bytes() == mock_response.content + + +def test_getStaticPage_invalid_write_method(tmp_path): + p = Pars() + filepath = str(tmp_path / 'page.html') + with pytest.raises(ValueError): + p.getStaticPage(filepath, 'http://example.com', writeMethod='r') diff --git a/tests/test_progress.py b/tests/test_progress.py new file mode 100644 index 0000000..3595a74 --- /dev/null +++ b/tests/test_progress.py @@ -0,0 +1,29 @@ +from ipars import ProgressBarManager + + +def test_full_cycle_does_not_raise(): + bar = ProgressBarManager(max=3) + bar.next() + bar.next() + bar.next() + bar.finish() + + +def test_single_iteration(): + bar = ProgressBarManager(max=1) + bar.next() + bar.finish() + + +def test_custom_message(): + bar = ProgressBarManager(max=2, message='Загрузка') + bar.next() + bar.next() + bar.finish() + + +def test_custom_fill_and_width(): + bar = ProgressBarManager(max=2, fill='=', width=20) + bar.next() + bar.next() + bar.finish() diff --git a/tests/test_timer.py b/tests/test_timer.py new file mode 100644 index 0000000..ce5a602 --- /dev/null +++ b/tests/test_timer.py @@ -0,0 +1,69 @@ +import time +import pytest +from ipars import TimerManager + + +def test_getWorkTime_seconds(): + timer = TimerManager() + timer.start() + time.sleep(0.1) + timer.end() + result = timer.getWorkTime('seconds') + assert 0.05 < result < 0.5 + + +def test_getWorkTime_minutes(): + timer = TimerManager() + timer.start() + time.sleep(0.1) + timer.end() + result = timer.getWorkTime('minutes') + assert result < 0.01 + + +def test_getWorkTime_hours(): + timer = TimerManager() + timer.start() + time.sleep(0.1) + timer.end() + result = timer.getWorkTime('hours') + assert result < 0.001 + + +def test_getWorkTime_ndigits(): + timer = TimerManager() + timer.start() + time.sleep(0.1) + timer.end() + result = timer.getWorkTime('seconds', ndigits=2) + assert isinstance(result, float) + assert len(str(result).split('.')[-1]) <= 2 + + +def test_getWorkTime_returns_float(): + timer = TimerManager() + timer.start() + timer.end() + result = timer.getWorkTime() + assert isinstance(result, float) + + +def test_raises_without_start(): + timer = TimerManager() + with pytest.raises(ValueError, match='"start"'): + timer.getWorkTime() + + +def test_raises_without_end(): + timer = TimerManager() + timer.start() + with pytest.raises(ValueError, match='"end"'): + timer.getWorkTime() + + +def test_raises_invalid_format(): + timer = TimerManager() + timer.start() + timer.end() + with pytest.raises(ValueError, match='milliseconds'): + timer.getWorkTime('milliseconds') diff --git a/tests/test_zip.py b/tests/test_zip.py new file mode 100644 index 0000000..3433a63 --- /dev/null +++ b/tests/test_zip.py @@ -0,0 +1,87 @@ +import zipfile +import pytest +from ipars import ZipManager + + +def test_zipfile_creates_archive(tmp_path): + zm = ZipManager() + source = tmp_path / 'hello.txt' + source.write_text('hello world') + zip_path = str(tmp_path / 'output.zip') + + zm.zipFile(str(source), zip_path) + + assert (tmp_path / 'output.zip').exists() + with zipfile.ZipFile(zip_path, 'r') as zf: + assert 'hello.txt' in zf.namelist() + + +def test_zipfile_content_is_correct(tmp_path): + zm = ZipManager() + source = tmp_path / 'data.txt' + source.write_text('test content') + zip_path = str(tmp_path / 'output.zip') + + zm.zipFile(str(source), zip_path) + + with zipfile.ZipFile(zip_path, 'r') as zf: + content = zf.read('data.txt').decode('utf-8') + assert content == 'test content' + + +def test_zipfolder_creates_archive(tmp_path): + zm = ZipManager() + folder = tmp_path / 'myfolder' + folder.mkdir() + (folder / 'a.txt').write_text('aaa') + (folder / 'b.txt').write_text('bbb') + zip_path = str(tmp_path / 'folder.zip') + + zm.zipFolder(str(folder), zip_path) + + assert (tmp_path / 'folder.zip').exists() + with zipfile.ZipFile(zip_path, 'r') as zf: + names = zf.namelist() + assert any('a.txt' in n for n in names) + assert any('b.txt' in n for n in names) + + +def test_zipfolder_nested(tmp_path): + zm = ZipManager() + folder = tmp_path / 'root' + folder.mkdir() + sub = folder / 'sub' + sub.mkdir() + (sub / 'deep.txt').write_text('deep') + zip_path = str(tmp_path / 'nested.zip') + + zm.zipFolder(str(folder), zip_path) + + with zipfile.ZipFile(zip_path, 'r') as zf: + names = zf.namelist() + assert any('deep.txt' in n for n in names) + + +def test_set_compression_none(): + zm = ZipManager('none') + assert zm.compression == zipfile.ZIP_STORED + + +def test_set_compression_normal(): + zm = ZipManager('normal') + assert zm.compression == zipfile.ZIP_DEFLATED + + +def test_set_compression_hard(): + zm = ZipManager('hard') + assert zm.compression == zipfile.ZIP_BZIP2 + + +def test_set_compression_maximum(): + zm = ZipManager('maximum') + assert zm.compression == zipfile.ZIP_LZMA + + +def test_invalid_compression_raises(): + with pytest.raises(ValueError): + ZipManager('ultra')