From d7a1f63867e579bb3438a4bbed0938859972a4d6 Mon Sep 17 00:00:00 2001 From: Nitin Awari Date: Mon, 20 Jan 2025 22:10:08 +0530 Subject: [PATCH 1/3] test coverage for api/core.py --- tests/api/test_core.py | 264 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 264 insertions(+) create mode 100644 tests/api/test_core.py diff --git a/tests/api/test_core.py b/tests/api/test_core.py new file mode 100644 index 000000000..5afb527a8 --- /dev/null +++ b/tests/api/test_core.py @@ -0,0 +1,264 @@ +import pytest +from flask import Flask +from werkzeug.exceptions import NotFound, Unauthorized +from unittest.mock import mock_open, patch, MagicMock +import os + +from nettacker.api.core import ( + get_value, + mime_types, + get_file, + api_key_is_valid, + languages_to_country, + graphs, + profiles, + scan_methods +) +from nettacker.config import Config +from nettacker.core.app import Nettacker + +# Constants for testing +TEST_API_KEY = 'test_key' +TEST_FILE_CONTENT = b'test content' +TEST_FILE_NAME = 'test.txt' + + +@pytest.fixture +def app(): + """Fixture providing configured Flask app for testing.""" + test_app = Flask(__name__) + test_app.config['OWASP_NETTACKER_CONFIG'] = {'api_access_key': TEST_API_KEY} + return test_app + + +@pytest.fixture +def mock_request(): + """Fixture providing mock Flask request with empty parameters.""" + mock = MagicMock() + mock.args = {} + mock.form = {} + mock.cookies = {} + return mock + + +def test_get_value_retrieves_from_args_when_present(mock_request): + """Test that get_value retrieves value from args when present.""" + expected_value = 'args_value' + mock_request.args = {'test_key': expected_value} + + result = get_value(mock_request, 'test_key') + + assert result == expected_value + + +def test_get_value_retrieves_from_form_when_args_empty(mock_request): + """Test that get_value retrieves value from form when args is empty.""" + expected_value = 'form_value' + mock_request.form = {'test_key': expected_value} + + result = get_value(mock_request, 'test_key') + + assert result == expected_value + + +def test_get_value_retrieves_from_cookies_when_others_empty(mock_request): + """Test that get_value retrieves value from cookies when args and form are empty.""" + expected_value = 'cookie_value' + mock_request.cookies = {'test_key': expected_value} + + result = get_value(mock_request, 'test_key') + + assert result == expected_value + + +def test_get_value_returns_empty_when_key_not_found(mock_request): + """Test that get_value returns empty string when key is not found.""" + result = get_value(mock_request, 'nonexistent_key') + + assert result == '' + + +def test_get_value_respects_source_precedence(mock_request): + """Test that get_value respects precedence: args > form > cookies.""" + mock_request.args = {'test_key': 'args_value'} + mock_request.form = {'test_key': 'form_value'} + mock_request.cookies = {'test_key': 'cookie_value'} + + result = get_value(mock_request, 'test_key') + + assert result == 'args_value' + + +def test_mime_types_returns_correct_mappings(): + """Test that mime_types returns correct content type mappings.""" + mime_dict = mime_types() + + # Verify return type + assert isinstance(mime_dict, dict) + + # Verify common mime types + assert mime_dict['.pdf'] == 'application/pdf' + assert mime_dict['.jpg'] == 'image/jpeg' + assert mime_dict['.html'] == 'text/html' + + # Verify dictionary has comprehensive mappings + assert len(mime_dict) > 50 + + +def test_get_file_returns_content_for_valid_path(): + """Test that get_file returns file content for valid path.""" + Config.path.web_static_dir = '/test/path' + test_file_path = os.path.join(str(Config.path.web_static_dir), TEST_FILE_NAME) + + with patch('builtins.open', mock_open(read_data=TEST_FILE_CONTENT)) as mock_file: + content = get_file(test_file_path) + + mock_file.assert_called_once_with(test_file_path, 'rb') + assert content == TEST_FILE_CONTENT + + +def test_get_file_raises_404_for_invalid_path(): + """Test that get_file raises 404 for paths outside web static directory.""" + Config.path.web_static_dir = '/test/path' + + with pytest.raises(NotFound): + get_file('/invalid/path/test.txt') + + +def test_get_file_raises_404_for_io_error(): + """Test that get_file raises 404 when IOError occurs.""" + Config.path.web_static_dir = '/test/path' + test_file_path = os.path.join(str(Config.path.web_static_dir), TEST_FILE_NAME) + + with patch('builtins.open', mock_open()) as mock_file: + mock_file.side_effect = IOError() + + with pytest.raises(NotFound): + get_file(test_file_path) + + +def test_get_file_raises_404_for_value_error(): + """Test that get_file raises 404 when ValueError occurs.""" + Config.path.web_static_dir = '/test/path' + test_file_path = os.path.join(str(Config.path.web_static_dir), TEST_FILE_NAME) + + with patch('builtins.open', mock_open()) as mock_file: + mock_file.side_effect = ValueError() + + with pytest.raises(NotFound): + get_file(test_file_path) + + +def test_api_key_valid_accepts_correct_key(app, mock_request): + """Test that api_key_is_valid accepts correct API key.""" + mock_request.args = {'key': TEST_API_KEY} + + result = api_key_is_valid(app, mock_request) + + assert result is None + + +def test_api_key_valid_raises_401_for_invalid_key(app, mock_request): + """Test that api_key_is_valid raises 401 for incorrect API key.""" + mock_request.args = {'key': 'wrong_key'} + + with pytest.raises(Unauthorized): + api_key_is_valid(app, mock_request) + + +@patch('nettacker.api.core.get_languages') +def test_languages_to_country_generates_correct_html(mock_get_languages): + """Test that languages_to_country generates correct HTML with flags.""" + test_languages = ['en', 'es', 'fr'] + mock_get_languages.return_value = test_languages + + result = languages_to_country() + + # Verify basic structure + assert isinstance(result, str) + + # Verify each language has correct flag and structure + expected_flags = {'en': 'us', 'es': 'es', 'fr': 'fr'} + for lang, flag in expected_flags.items(): + assert f'flag-icon-{flag}' in result + assert f'value="{lang}"' in result + + # Verify English is selected by default + assert 'selected' in result and 'value="en"' in result + + +@patch.object(Nettacker, 'load_graphs') +def test_graphs_generates_correct_html(mock_load_graphs): + """Test that graphs generates correct HTML with all graph options.""" + test_graphs = ['d3_tree', 'd3_force'] + mock_load_graphs.return_value = test_graphs + + result = graphs() + + # Verify basic structure + assert isinstance(result, str) + + # Verify 'None' option exists + assert 'value=""' in result + assert 'None' in result + + # Verify each graph option exists + for graph in test_graphs: + assert f'value="{graph}"' in result + assert f'class="radio"' in result + + +@patch.object(Nettacker, 'load_profiles') +def test_profiles_generates_correct_html(mock_load_profiles): + """Test that profiles generates correct HTML with appropriate styling.""" + test_profiles = { + 'scan': {}, + 'brute': {}, + 'vulnerability': {} + } + mock_load_profiles.return_value = test_profiles + + result = profiles() + + # Verify basic structure + assert isinstance(result, str) + + # Verify each profile has correct label and class + style_map = { + 'scan': 'success', + 'brute': 'warning', + 'vulnerability': 'danger' + } + + for profile, style in style_map.items(): + assert f'class="checkbox checkbox-{profile}"' in result + assert f'label label-{style}"' in result + assert profile in result + + +@patch.object(Nettacker, 'load_modules') +def test_scan_methods_generates_correct_html(mock_load_modules): + """Test that scan_methods generates correct HTML with appropriate styling.""" + test_modules = { + 'all': {}, + 'port_scan': {}, + 'ssh_brute': {}, + 'wp_vuln': {} + } + mock_load_modules.return_value = test_modules + + result = scan_methods() + + # Verify basic structure + assert isinstance(result, str) + + # Verify 'all' module is excluded + assert 'all' not in result + + # Verify each module has correct styling + assert 'port_scan' in result + assert 'label-success' in result # for _scan + assert 'ssh_brute' in result + assert 'label-warning' in result # for _brute + assert 'wp_vuln' in result + assert 'label-danger' in result # for _vuln \ No newline at end of file From 6c7f75bc09d660b026f90e4dfb79f1eb0801efb6 Mon Sep 17 00:00:00 2001 From: Nitin Awari Date: Mon, 20 Jan 2025 22:20:07 +0530 Subject: [PATCH 2/3] bug-fixes --- tests/api/test_core.py | 206 +++++++++++++++++++---------------------- 1 file changed, 97 insertions(+), 109 deletions(-) diff --git a/tests/api/test_core.py b/tests/api/test_core.py index 5afb527a8..4d7b98613 100644 --- a/tests/api/test_core.py +++ b/tests/api/test_core.py @@ -1,8 +1,9 @@ +import os +from unittest.mock import mock_open, patch, MagicMock + import pytest from flask import Flask from werkzeug.exceptions import NotFound, Unauthorized -from unittest.mock import mock_open, patch, MagicMock -import os from nettacker.api.core import ( get_value, @@ -12,22 +13,22 @@ languages_to_country, graphs, profiles, - scan_methods + scan_methods, ) from nettacker.config import Config from nettacker.core.app import Nettacker # Constants for testing -TEST_API_KEY = 'test_key' -TEST_FILE_CONTENT = b'test content' -TEST_FILE_NAME = 'test.txt' +TEST_API_KEY = "test_key" +TEST_FILE_CONTENT = b"test content" +TEST_FILE_NAME = "test.txt" @pytest.fixture def app(): """Fixture providing configured Flask app for testing.""" test_app = Flask(__name__) - test_app.config['OWASP_NETTACKER_CONFIG'] = {'api_access_key': TEST_API_KEY} + test_app.config["OWASP_NETTACKER_CONFIG"] = {"api_access_key": TEST_API_KEY} return test_app @@ -43,222 +44,209 @@ def mock_request(): def test_get_value_retrieves_from_args_when_present(mock_request): """Test that get_value retrieves value from args when present.""" - expected_value = 'args_value' - mock_request.args = {'test_key': expected_value} - - result = get_value(mock_request, 'test_key') - + expected_value = "args_value" + mock_request.args = {"test_key": expected_value} + + result = get_value(mock_request, "test_key") + assert result == expected_value def test_get_value_retrieves_from_form_when_args_empty(mock_request): """Test that get_value retrieves value from form when args is empty.""" - expected_value = 'form_value' - mock_request.form = {'test_key': expected_value} - - result = get_value(mock_request, 'test_key') - + expected_value = "form_value" + mock_request.form = {"test_key": expected_value} + + result = get_value(mock_request, "test_key") + assert result == expected_value def test_get_value_retrieves_from_cookies_when_others_empty(mock_request): """Test that get_value retrieves value from cookies when args and form are empty.""" - expected_value = 'cookie_value' - mock_request.cookies = {'test_key': expected_value} - - result = get_value(mock_request, 'test_key') - + expected_value = "cookie_value" + mock_request.cookies = {"test_key": expected_value} + + result = get_value(mock_request, "test_key") + assert result == expected_value def test_get_value_returns_empty_when_key_not_found(mock_request): """Test that get_value returns empty string when key is not found.""" - result = get_value(mock_request, 'nonexistent_key') - - assert result == '' + result = get_value(mock_request, "nonexistent_key") + + assert result == "" def test_get_value_respects_source_precedence(mock_request): """Test that get_value respects precedence: args > form > cookies.""" - mock_request.args = {'test_key': 'args_value'} - mock_request.form = {'test_key': 'form_value'} - mock_request.cookies = {'test_key': 'cookie_value'} - - result = get_value(mock_request, 'test_key') - - assert result == 'args_value' + mock_request.args = {"test_key": "args_value"} + mock_request.form = {"test_key": "form_value"} + mock_request.cookies = {"test_key": "cookie_value"} + + result = get_value(mock_request, "test_key") + + assert result == "args_value" def test_mime_types_returns_correct_mappings(): """Test that mime_types returns correct content type mappings.""" mime_dict = mime_types() - + # Verify return type assert isinstance(mime_dict, dict) - + # Verify common mime types - assert mime_dict['.pdf'] == 'application/pdf' - assert mime_dict['.jpg'] == 'image/jpeg' - assert mime_dict['.html'] == 'text/html' - + assert mime_dict[".pdf"] == "application/pdf" + assert mime_dict[".jpg"] == "image/jpeg" + assert mime_dict[".html"] == "text/html" + # Verify dictionary has comprehensive mappings assert len(mime_dict) > 50 def test_get_file_returns_content_for_valid_path(): """Test that get_file returns file content for valid path.""" - Config.path.web_static_dir = '/test/path' + Config.path.web_static_dir = "/test/path" test_file_path = os.path.join(str(Config.path.web_static_dir), TEST_FILE_NAME) - - with patch('builtins.open', mock_open(read_data=TEST_FILE_CONTENT)) as mock_file: + + with patch("builtins.open", mock_open(read_data=TEST_FILE_CONTENT)) as mock_file: content = get_file(test_file_path) - - mock_file.assert_called_once_with(test_file_path, 'rb') + + mock_file.assert_called_once_with(test_file_path, "rb") assert content == TEST_FILE_CONTENT def test_get_file_raises_404_for_invalid_path(): """Test that get_file raises 404 for paths outside web static directory.""" - Config.path.web_static_dir = '/test/path' - + Config.path.web_static_dir = "/test/path" + with pytest.raises(NotFound): - get_file('/invalid/path/test.txt') + get_file("/invalid/path/test.txt") def test_get_file_raises_404_for_io_error(): """Test that get_file raises 404 when IOError occurs.""" - Config.path.web_static_dir = '/test/path' + Config.path.web_static_dir = "/test/path" test_file_path = os.path.join(str(Config.path.web_static_dir), TEST_FILE_NAME) - - with patch('builtins.open', mock_open()) as mock_file: + + with patch("builtins.open", mock_open()) as mock_file: mock_file.side_effect = IOError() - + with pytest.raises(NotFound): get_file(test_file_path) def test_get_file_raises_404_for_value_error(): """Test that get_file raises 404 when ValueError occurs.""" - Config.path.web_static_dir = '/test/path' + Config.path.web_static_dir = "/test/path" test_file_path = os.path.join(str(Config.path.web_static_dir), TEST_FILE_NAME) - - with patch('builtins.open', mock_open()) as mock_file: + + with patch("builtins.open", mock_open()) as mock_file: mock_file.side_effect = ValueError() - + with pytest.raises(NotFound): get_file(test_file_path) def test_api_key_valid_accepts_correct_key(app, mock_request): """Test that api_key_is_valid accepts correct API key.""" - mock_request.args = {'key': TEST_API_KEY} - + mock_request.args = {"key": TEST_API_KEY} + result = api_key_is_valid(app, mock_request) - + assert result is None def test_api_key_valid_raises_401_for_invalid_key(app, mock_request): """Test that api_key_is_valid raises 401 for incorrect API key.""" - mock_request.args = {'key': 'wrong_key'} - + mock_request.args = {"key": "wrong_key"} + with pytest.raises(Unauthorized): api_key_is_valid(app, mock_request) -@patch('nettacker.api.core.get_languages') +@patch("nettacker.api.core.get_languages") def test_languages_to_country_generates_correct_html(mock_get_languages): """Test that languages_to_country generates correct HTML with flags.""" - test_languages = ['en', 'es', 'fr'] + test_languages = ["en", "es", "fr"] mock_get_languages.return_value = test_languages - + result = languages_to_country() - + # Verify basic structure assert isinstance(result, str) - + # Verify each language has correct flag and structure - expected_flags = {'en': 'us', 'es': 'es', 'fr': 'fr'} + expected_flags = {"en": "us", "es": "es", "fr": "fr"} for lang, flag in expected_flags.items(): - assert f'flag-icon-{flag}' in result + assert f"flag-icon-{flag}" in result assert f'value="{lang}"' in result - + # Verify English is selected by default - assert 'selected' in result and 'value="en"' in result + assert "selected" in result and 'value="en"' in result -@patch.object(Nettacker, 'load_graphs') +@patch.object(Nettacker, "load_graphs") def test_graphs_generates_correct_html(mock_load_graphs): """Test that graphs generates correct HTML with all graph options.""" - test_graphs = ['d3_tree', 'd3_force'] + test_graphs = ["d3_tree", "d3_force"] mock_load_graphs.return_value = test_graphs - + result = graphs() - + # Verify basic structure assert isinstance(result, str) - + # Verify 'None' option exists assert 'value=""' in result - assert 'None' in result - + assert "None" in result + # Verify each graph option exists for graph in test_graphs: assert f'value="{graph}"' in result - assert f'class="radio"' in result + assert 'class="radio"' in result -@patch.object(Nettacker, 'load_profiles') +@patch.object(Nettacker, "load_profiles") def test_profiles_generates_correct_html(mock_load_profiles): """Test that profiles generates correct HTML with appropriate styling.""" - test_profiles = { - 'scan': {}, - 'brute': {}, - 'vulnerability': {} - } + test_profiles = {"scan": {}, "brute": {}, "vulnerability": {}} mock_load_profiles.return_value = test_profiles - + result = profiles() - + # Verify basic structure assert isinstance(result, str) - + # Verify each profile has correct label and class - style_map = { - 'scan': 'success', - 'brute': 'warning', - 'vulnerability': 'danger' - } - + style_map = {"scan": "success", "brute": "warning", "vulnerability": "danger"} + for profile, style in style_map.items(): assert f'class="checkbox checkbox-{profile}"' in result assert f'label label-{style}"' in result assert profile in result -@patch.object(Nettacker, 'load_modules') +@patch.object(Nettacker, "load_modules") def test_scan_methods_generates_correct_html(mock_load_modules): """Test that scan_methods generates correct HTML with appropriate styling.""" - test_modules = { - 'all': {}, - 'port_scan': {}, - 'ssh_brute': {}, - 'wp_vuln': {} - } + test_modules = {"all": {}, "port_scan": {}, "ssh_brute": {}, "wp_vuln": {}} mock_load_modules.return_value = test_modules - + result = scan_methods() - + # Verify basic structure assert isinstance(result, str) - + # Verify 'all' module is excluded - assert 'all' not in result - + assert "all" not in result + # Verify each module has correct styling - assert 'port_scan' in result - assert 'label-success' in result # for _scan - assert 'ssh_brute' in result - assert 'label-warning' in result # for _brute - assert 'wp_vuln' in result - assert 'label-danger' in result # for _vuln \ No newline at end of file + assert "port_scan" in result + assert "label-success" in result # for _scan + assert "ssh_brute" in result + assert "label-warning" in result # for _brute + assert "wp_vuln" in result + assert "label-danger" in result # for _vuln From a73dda0f39678295c5596aaee5aebda269e3e85a Mon Sep 17 00:00:00 2001 From: Nitin Awari Date: Mon, 20 Jan 2025 22:38:50 +0530 Subject: [PATCH 3/3] test-cases update --- tests/api/test_core.py | 21 +++------------------ 1 file changed, 3 insertions(+), 18 deletions(-) diff --git a/tests/api/test_core.py b/tests/api/test_core.py index 4d7b98613..2e14f4f69 100644 --- a/tests/api/test_core.py +++ b/tests/api/test_core.py @@ -18,7 +18,6 @@ from nettacker.config import Config from nettacker.core.app import Nettacker -# Constants for testing TEST_API_KEY = "test_key" TEST_FILE_CONTENT = b"test content" TEST_FILE_NAME = "test.txt" @@ -94,15 +93,12 @@ def test_mime_types_returns_correct_mappings(): """Test that mime_types returns correct content type mappings.""" mime_dict = mime_types() - # Verify return type assert isinstance(mime_dict, dict) - # Verify common mime types assert mime_dict[".pdf"] == "application/pdf" assert mime_dict[".jpg"] == "image/jpeg" assert mime_dict[".html"] == "text/html" - # Verify dictionary has comprehensive mappings assert len(mime_dict) > 50 @@ -175,16 +171,13 @@ def test_languages_to_country_generates_correct_html(mock_get_languages): result = languages_to_country() - # Verify basic structure assert isinstance(result, str) - # Verify each language has correct flag and structure expected_flags = {"en": "us", "es": "es", "fr": "fr"} for lang, flag in expected_flags.items(): assert f"flag-icon-{flag}" in result assert f'value="{lang}"' in result - # Verify English is selected by default assert "selected" in result and 'value="en"' in result @@ -196,14 +189,11 @@ def test_graphs_generates_correct_html(mock_load_graphs): result = graphs() - # Verify basic structure assert isinstance(result, str) - # Verify 'None' option exists assert 'value=""' in result assert "None" in result - # Verify each graph option exists for graph in test_graphs: assert f'value="{graph}"' in result assert 'class="radio"' in result @@ -217,10 +207,8 @@ def test_profiles_generates_correct_html(mock_load_profiles): result = profiles() - # Verify basic structure assert isinstance(result, str) - # Verify each profile has correct label and class style_map = {"scan": "success", "brute": "warning", "vulnerability": "danger"} for profile, style in style_map.items(): @@ -237,16 +225,13 @@ def test_scan_methods_generates_correct_html(mock_load_modules): result = scan_methods() - # Verify basic structure assert isinstance(result, str) - # Verify 'all' module is excluded assert "all" not in result - # Verify each module has correct styling assert "port_scan" in result - assert "label-success" in result # for _scan + assert "label-success" in result assert "ssh_brute" in result - assert "label-warning" in result # for _brute + assert "label-warning" in result assert "wp_vuln" in result - assert "label-danger" in result # for _vuln + assert "label-danger" in result