lanscape 2.0.0a2__tar.gz → 2.0.0b1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of lanscape might be problematic. Click here for more details.
- {lanscape-2.0.0a2/lanscape.egg-info → lanscape-2.0.0b1}/PKG-INFO +2 -2
- {lanscape-2.0.0a2 → lanscape-2.0.0b1}/README.md +1 -1
- {lanscape-2.0.0a2 → lanscape-2.0.0b1}/lanscape/core/device_alive.py +1 -1
- {lanscape-2.0.0a2 → lanscape-2.0.0b1}/lanscape/core/ip_parser.py +1 -25
- {lanscape-2.0.0a2 → lanscape-2.0.0b1}/lanscape/core/service_scan.py +3 -20
- {lanscape-2.0.0a2 → lanscape-2.0.0b1}/lanscape/ui/templates/scan/config.html +1 -1
- {lanscape-2.0.0a2 → lanscape-2.0.0b1/lanscape.egg-info}/PKG-INFO +2 -2
- {lanscape-2.0.0a2 → lanscape-2.0.0b1}/pyproject.toml +1 -1
- {lanscape-2.0.0a2 → lanscape-2.0.0b1}/tests/test_api.py +23 -16
- {lanscape-2.0.0a2 → lanscape-2.0.0b1}/tests/test_decorators.py +7 -6
- {lanscape-2.0.0a2 → lanscape-2.0.0b1}/tests/test_env.py +20 -20
- {lanscape-2.0.0a2 → lanscape-2.0.0b1}/tests/test_library.py +2 -1
- {lanscape-2.0.0a2 → lanscape-2.0.0b1}/tests/test_logging.py +15 -14
- {lanscape-2.0.0a2 → lanscape-2.0.0b1}/tests/test_port_scan.py +9 -3
- {lanscape-2.0.0a2 → lanscape-2.0.0b1}/tests/test_service_scan.py +6 -19
- {lanscape-2.0.0a2 → lanscape-2.0.0b1}/tests/test_utils.py +13 -12
- {lanscape-2.0.0a2 → lanscape-2.0.0b1}/LICENSE +0 -0
- {lanscape-2.0.0a2 → lanscape-2.0.0b1}/MANIFEST.in +0 -0
- {lanscape-2.0.0a2 → lanscape-2.0.0b1}/lanscape/__init__.py +0 -0
- {lanscape-2.0.0a2 → lanscape-2.0.0b1}/lanscape/__main__.py +0 -0
- {lanscape-2.0.0a2 → lanscape-2.0.0b1}/lanscape/core/__init__.py +0 -0
- {lanscape-2.0.0a2 → lanscape-2.0.0b1}/lanscape/core/app_scope.py +0 -0
- {lanscape-2.0.0a2 → lanscape-2.0.0b1}/lanscape/core/decorators.py +0 -0
- {lanscape-2.0.0a2 → lanscape-2.0.0b1}/lanscape/core/errors.py +0 -0
- {lanscape-2.0.0a2 → lanscape-2.0.0b1}/lanscape/core/logger.py +0 -0
- {lanscape-2.0.0a2 → lanscape-2.0.0b1}/lanscape/core/mac_lookup.py +0 -0
- {lanscape-2.0.0a2 → lanscape-2.0.0b1}/lanscape/core/net_tools.py +0 -0
- {lanscape-2.0.0a2 → lanscape-2.0.0b1}/lanscape/core/port_manager.py +0 -0
- {lanscape-2.0.0a2 → lanscape-2.0.0b1}/lanscape/core/runtime_args.py +0 -0
- {lanscape-2.0.0a2 → lanscape-2.0.0b1}/lanscape/core/scan_config.py +0 -0
- {lanscape-2.0.0a2 → lanscape-2.0.0b1}/lanscape/core/subnet_scan.py +0 -0
- {lanscape-2.0.0a2 → lanscape-2.0.0b1}/lanscape/core/version_manager.py +0 -0
- {lanscape-2.0.0a2 → lanscape-2.0.0b1}/lanscape/core/web_browser.py +0 -0
- {lanscape-2.0.0a2 → lanscape-2.0.0b1}/lanscape/resources/mac_addresses/convert_csv.py +0 -0
- {lanscape-2.0.0a2 → lanscape-2.0.0b1}/lanscape/resources/mac_addresses/mac_db.json +0 -0
- {lanscape-2.0.0a2 → lanscape-2.0.0b1}/lanscape/resources/ports/convert_csv.py +0 -0
- {lanscape-2.0.0a2 → lanscape-2.0.0b1}/lanscape/resources/ports/full.json +0 -0
- {lanscape-2.0.0a2 → lanscape-2.0.0b1}/lanscape/resources/ports/large.json +0 -0
- {lanscape-2.0.0a2 → lanscape-2.0.0b1}/lanscape/resources/ports/medium.json +0 -0
- {lanscape-2.0.0a2 → lanscape-2.0.0b1}/lanscape/resources/ports/small.json +0 -0
- {lanscape-2.0.0a2 → lanscape-2.0.0b1}/lanscape/resources/ports/test_port_list_scan.json +0 -0
- {lanscape-2.0.0a2 → lanscape-2.0.0b1}/lanscape/resources/services/definitions.jsonc +0 -0
- {lanscape-2.0.0a2 → lanscape-2.0.0b1}/lanscape/ui/__init__.py +0 -0
- {lanscape-2.0.0a2 → lanscape-2.0.0b1}/lanscape/ui/app.py +0 -0
- {lanscape-2.0.0a2 → lanscape-2.0.0b1}/lanscape/ui/blueprints/__init__.py +0 -0
- {lanscape-2.0.0a2 → lanscape-2.0.0b1}/lanscape/ui/blueprints/api/__init__.py +0 -0
- {lanscape-2.0.0a2 → lanscape-2.0.0b1}/lanscape/ui/blueprints/api/port.py +0 -0
- {lanscape-2.0.0a2 → lanscape-2.0.0b1}/lanscape/ui/blueprints/api/scan.py +0 -0
- {lanscape-2.0.0a2 → lanscape-2.0.0b1}/lanscape/ui/blueprints/api/tools.py +0 -0
- {lanscape-2.0.0a2 → lanscape-2.0.0b1}/lanscape/ui/blueprints/web/__init__.py +0 -0
- {lanscape-2.0.0a2 → lanscape-2.0.0b1}/lanscape/ui/blueprints/web/routes.py +0 -0
- {lanscape-2.0.0a2 → lanscape-2.0.0b1}/lanscape/ui/main.py +0 -0
- {lanscape-2.0.0a2 → lanscape-2.0.0b1}/lanscape/ui/shutdown_handler.py +0 -0
- {lanscape-2.0.0a2 → lanscape-2.0.0b1}/lanscape/ui/static/css/style.css +0 -0
- {lanscape-2.0.0a2 → lanscape-2.0.0b1}/lanscape/ui/static/img/ico/android-chrome-192x192.png +0 -0
- {lanscape-2.0.0a2 → lanscape-2.0.0b1}/lanscape/ui/static/img/ico/android-chrome-512x512.png +0 -0
- {lanscape-2.0.0a2 → lanscape-2.0.0b1}/lanscape/ui/static/img/ico/apple-touch-icon.png +0 -0
- {lanscape-2.0.0a2 → lanscape-2.0.0b1}/lanscape/ui/static/img/ico/favicon-16x16.png +0 -0
- {lanscape-2.0.0a2 → lanscape-2.0.0b1}/lanscape/ui/static/img/ico/favicon-32x32.png +0 -0
- {lanscape-2.0.0a2 → lanscape-2.0.0b1}/lanscape/ui/static/img/ico/favicon.ico +0 -0
- {lanscape-2.0.0a2 → lanscape-2.0.0b1}/lanscape/ui/static/img/ico/site.webmanifest +0 -0
- {lanscape-2.0.0a2 → lanscape-2.0.0b1}/lanscape/ui/static/js/core.js +0 -0
- {lanscape-2.0.0a2 → lanscape-2.0.0b1}/lanscape/ui/static/js/layout-sizing.js +0 -0
- {lanscape-2.0.0a2 → lanscape-2.0.0b1}/lanscape/ui/static/js/main.js +0 -0
- {lanscape-2.0.0a2 → lanscape-2.0.0b1}/lanscape/ui/static/js/on-tab-close.js +0 -0
- {lanscape-2.0.0a2 → lanscape-2.0.0b1}/lanscape/ui/static/js/quietReload.js +0 -0
- {lanscape-2.0.0a2 → lanscape-2.0.0b1}/lanscape/ui/static/js/scan-config.js +0 -0
- {lanscape-2.0.0a2 → lanscape-2.0.0b1}/lanscape/ui/static/js/shutdown-server.js +0 -0
- {lanscape-2.0.0a2 → lanscape-2.0.0b1}/lanscape/ui/static/js/subnet-info.js +0 -0
- {lanscape-2.0.0a2 → lanscape-2.0.0b1}/lanscape/ui/static/js/subnet-selector.js +0 -0
- {lanscape-2.0.0a2 → lanscape-2.0.0b1}/lanscape/ui/static/lanscape.webmanifest +0 -0
- {lanscape-2.0.0a2 → lanscape-2.0.0b1}/lanscape/ui/templates/base.html +0 -0
- {lanscape-2.0.0a2 → lanscape-2.0.0b1}/lanscape/ui/templates/core/head.html +0 -0
- {lanscape-2.0.0a2 → lanscape-2.0.0b1}/lanscape/ui/templates/core/scripts.html +0 -0
- {lanscape-2.0.0a2 → lanscape-2.0.0b1}/lanscape/ui/templates/error.html +0 -0
- {lanscape-2.0.0a2 → lanscape-2.0.0b1}/lanscape/ui/templates/info.html +0 -0
- {lanscape-2.0.0a2 → lanscape-2.0.0b1}/lanscape/ui/templates/main.html +0 -0
- {lanscape-2.0.0a2 → lanscape-2.0.0b1}/lanscape/ui/templates/scan/device-detail.html +0 -0
- {lanscape-2.0.0a2 → lanscape-2.0.0b1}/lanscape/ui/templates/scan/export.html +0 -0
- {lanscape-2.0.0a2 → lanscape-2.0.0b1}/lanscape/ui/templates/scan/ip-table-row.html +0 -0
- {lanscape-2.0.0a2 → lanscape-2.0.0b1}/lanscape/ui/templates/scan/ip-table.html +0 -0
- {lanscape-2.0.0a2 → lanscape-2.0.0b1}/lanscape/ui/templates/scan/overview.html +0 -0
- {lanscape-2.0.0a2 → lanscape-2.0.0b1}/lanscape/ui/templates/scan/scan-error.html +0 -0
- {lanscape-2.0.0a2 → lanscape-2.0.0b1}/lanscape/ui/templates/scan.html +0 -0
- {lanscape-2.0.0a2 → lanscape-2.0.0b1}/lanscape/ui/templates/shutdown.html +0 -0
- {lanscape-2.0.0a2 → lanscape-2.0.0b1}/lanscape.egg-info/SOURCES.txt +0 -0
- {lanscape-2.0.0a2 → lanscape-2.0.0b1}/lanscape.egg-info/dependency_links.txt +0 -0
- {lanscape-2.0.0a2 → lanscape-2.0.0b1}/lanscape.egg-info/entry_points.txt +0 -0
- {lanscape-2.0.0a2 → lanscape-2.0.0b1}/lanscape.egg-info/requires.txt +0 -0
- {lanscape-2.0.0a2 → lanscape-2.0.0b1}/lanscape.egg-info/top_level.txt +0 -0
- {lanscape-2.0.0a2 → lanscape-2.0.0b1}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: lanscape
|
|
3
|
-
Version: 2.0.
|
|
3
|
+
Version: 2.0.0b1
|
|
4
4
|
Summary: A python based local network scanner
|
|
5
5
|
Author-email: Michael Dennis <michael@dipduo.com>
|
|
6
6
|
License-Expression: MIT
|
|
@@ -88,7 +88,7 @@ I use a combination of ARP, ICMP & port testing to determine if a device is onli
|
|
|
88
88
|
Recommendations:
|
|
89
89
|
|
|
90
90
|
- Adjust scan configuration
|
|
91
|
-
- Configure ARP lookup [ARP lookup setup](./
|
|
91
|
+
- Configure ARP lookup [ARP lookup setup](./docs/arp-issues.md)
|
|
92
92
|
- Create a bug
|
|
93
93
|
|
|
94
94
|
|
|
@@ -56,7 +56,7 @@ I use a combination of ARP, ICMP & port testing to determine if a device is onli
|
|
|
56
56
|
Recommendations:
|
|
57
57
|
|
|
58
58
|
- Adjust scan configuration
|
|
59
|
-
- Configure ARP lookup [ARP lookup setup](./
|
|
59
|
+
- Configure ARP lookup [ARP lookup setup](./docs/arp-issues.md)
|
|
60
60
|
- Create a bug
|
|
61
61
|
|
|
62
62
|
|
|
@@ -10,7 +10,6 @@ This module provides utilities for parsing various IP address formats including:
|
|
|
10
10
|
It also includes validation to prevent processing excessively large IP ranges.
|
|
11
11
|
"""
|
|
12
12
|
import ipaddress
|
|
13
|
-
import re
|
|
14
13
|
|
|
15
14
|
from lanscape.core.errors import SubnetTooLargeError
|
|
16
15
|
|
|
@@ -50,14 +49,10 @@ def parse_ip_input(ip_input):
|
|
|
50
49
|
for ip in net.hosts():
|
|
51
50
|
ip_ranges.append(ip)
|
|
52
51
|
|
|
53
|
-
# Handle IP range (e.g., 10.0.0.15-10.0.0.25)
|
|
52
|
+
# Handle IP range (e.g., 10.0.0.15-10.0.0.25) and (e.g., 10.0.9.1-253)
|
|
54
53
|
elif '-' in entry:
|
|
55
54
|
ip_ranges += parse_ip_range(entry)
|
|
56
55
|
|
|
57
|
-
# Handle shorthand IP range (e.g., 10.0.9.1-253)
|
|
58
|
-
elif re.search(r'\d+\-\d+', entry):
|
|
59
|
-
ip_ranges += parse_shorthand_ip_range(entry)
|
|
60
|
-
|
|
61
56
|
# If no CIDR or range, assume a single IP
|
|
62
57
|
else:
|
|
63
58
|
ip_ranges.append(ipaddress.IPv4Address(entry))
|
|
@@ -106,25 +101,6 @@ def parse_ip_range(entry):
|
|
|
106
101
|
return list(ip_range_to_list(start_ip, end_ip))
|
|
107
102
|
|
|
108
103
|
|
|
109
|
-
def parse_shorthand_ip_range(entry):
|
|
110
|
-
"""
|
|
111
|
-
Parse a shorthand IP range (e.g., 192.168.1.1-10).
|
|
112
|
-
|
|
113
|
-
In this format, only the last octet of the end IP is specified.
|
|
114
|
-
|
|
115
|
-
Args:
|
|
116
|
-
entry (str): String containing a shorthand IP range
|
|
117
|
-
|
|
118
|
-
Returns:
|
|
119
|
-
list: List of IPv4Address objects in the range (inclusive)
|
|
120
|
-
"""
|
|
121
|
-
start_ip, end_part = entry.split('-')
|
|
122
|
-
start_ip = ipaddress.IPv4Address(start_ip.strip())
|
|
123
|
-
end_ip = start_ip.exploded.rsplit('.', 1)[0] + '.' + end_part.strip()
|
|
124
|
-
|
|
125
|
-
return list(ip_range_to_list(start_ip, ipaddress.IPv4Address(end_ip)))
|
|
126
|
-
|
|
127
|
-
|
|
128
104
|
def ip_range_to_list(start_ip, end_ip):
|
|
129
105
|
"""
|
|
130
106
|
Convert an IP range defined by start and end addresses to a list of addresses.
|
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
"""
|
|
3
3
|
|
|
4
4
|
from typing import Optional, Union
|
|
5
|
-
import sys
|
|
6
5
|
import asyncio
|
|
7
6
|
import logging
|
|
8
7
|
import traceback
|
|
@@ -10,6 +9,9 @@ import traceback
|
|
|
10
9
|
from lanscape.core.app_scope import ResourceManager
|
|
11
10
|
from lanscape.core.scan_config import ServiceScanConfig, ServiceScanStrategy
|
|
12
11
|
|
|
12
|
+
# asyncio complains more than it needs to
|
|
13
|
+
logging.getLogger("asyncio").setLevel(logging.WARNING)
|
|
14
|
+
|
|
13
15
|
log = logging.getLogger('ServiceScan')
|
|
14
16
|
SERVICES = ResourceManager('services').get_jsonc('definitions.jsonc')
|
|
15
17
|
|
|
@@ -183,22 +185,3 @@ def scan_service(ip: str, port: int, cfg: ServiceScanConfig) -> str:
|
|
|
183
185
|
|
|
184
186
|
# Use asyncio.run to execute the asynchronous logic synchronously
|
|
185
187
|
return asyncio.run(_async_scan_service(ip, port, cfg=cfg))
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
def asyncio_logger_suppression():
|
|
189
|
-
"""Suppress the noisy asyncio transport errors since they are expected in service scanning."""
|
|
190
|
-
|
|
191
|
-
# Reduce noisy asyncio transport errors on Windows by switching to Selector policy
|
|
192
|
-
if sys.platform.startswith("win"):
|
|
193
|
-
try:
|
|
194
|
-
asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy())
|
|
195
|
-
except Exception:
|
|
196
|
-
pass
|
|
197
|
-
# Also tone down asyncio logger noise from transport callbacks
|
|
198
|
-
try:
|
|
199
|
-
logging.getLogger("asyncio").setLevel(logging.WARNING)
|
|
200
|
-
except Exception:
|
|
201
|
-
pass
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
asyncio_logger_suppression()
|
|
@@ -56,7 +56,7 @@
|
|
|
56
56
|
data-bs-toggle="tooltip"
|
|
57
57
|
data-bs-placement="top"
|
|
58
58
|
title="ARP lookup is not supported on this system, click for more info"
|
|
59
|
-
onclick="window.open('https://github.com/mdennis281/LANscape/blob/main/
|
|
59
|
+
onclick="window.open('https://github.com/mdennis281/LANscape/blob/main/docs/arp-issues.md', '_blank')"
|
|
60
60
|
>
|
|
61
61
|
help
|
|
62
62
|
</span>
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: lanscape
|
|
3
|
-
Version: 2.0.
|
|
3
|
+
Version: 2.0.0b1
|
|
4
4
|
Summary: A python based local network scanner
|
|
5
5
|
Author-email: Michael Dennis <michael@dipduo.com>
|
|
6
6
|
License-Expression: MIT
|
|
@@ -88,7 +88,7 @@ I use a combination of ARP, ICMP & port testing to determine if a device is onli
|
|
|
88
88
|
Recommendations:
|
|
89
89
|
|
|
90
90
|
- Adjust scan configuration
|
|
91
|
-
- Configure ARP lookup [ARP lookup setup](./
|
|
91
|
+
- Configure ARP lookup [ARP lookup setup](./docs/arp-issues.md)
|
|
92
92
|
- Create a bug
|
|
93
93
|
|
|
94
94
|
|
|
@@ -4,9 +4,10 @@ Tests REST API endpoints for port management, subnet validation, and scan operat
|
|
|
4
4
|
"""
|
|
5
5
|
import json
|
|
6
6
|
import time
|
|
7
|
-
import pytest
|
|
8
7
|
from unittest.mock import patch
|
|
9
8
|
|
|
9
|
+
import pytest
|
|
10
|
+
|
|
10
11
|
from lanscape.ui.app import app
|
|
11
12
|
from lanscape.core.net_tools import get_network_subnet
|
|
12
13
|
|
|
@@ -43,13 +44,14 @@ def test_scan_config():
|
|
|
43
44
|
# API Port Management Tests
|
|
44
45
|
###########################
|
|
45
46
|
|
|
47
|
+
|
|
46
48
|
def test_port_lifecycle(api_client, sample_port_list, updated_port_list):
|
|
47
49
|
"""
|
|
48
50
|
Test the complete lifecycle of port list management through the API.
|
|
49
51
|
Creates, retrieves, updates, and deletes a port list through API endpoints.
|
|
50
52
|
"""
|
|
51
53
|
test_list_name = 'test_port_list_lifecycle'
|
|
52
|
-
|
|
54
|
+
|
|
53
55
|
# Delete the new port list if it exists
|
|
54
56
|
api_client.delete(f'/api/port/list/{test_list_name}')
|
|
55
57
|
|
|
@@ -94,6 +96,7 @@ def test_port_lifecycle(api_client, sample_port_list, updated_port_list):
|
|
|
94
96
|
# API Scan Tests
|
|
95
97
|
################
|
|
96
98
|
|
|
99
|
+
|
|
97
100
|
@pytest.mark.integration
|
|
98
101
|
def test_scan(api_client, sample_port_list, test_scan_config):
|
|
99
102
|
"""
|
|
@@ -101,7 +104,7 @@ def test_scan(api_client, sample_port_list, test_scan_config):
|
|
|
101
104
|
Verifies scan creation, status retrieval, and UI rendering for scan results.
|
|
102
105
|
"""
|
|
103
106
|
test_list_name = 'test_port_list_scan'
|
|
104
|
-
|
|
107
|
+
|
|
105
108
|
# Delete the new port list if it exists
|
|
106
109
|
api_client.delete(f'/api/port/list/{test_list_name}')
|
|
107
110
|
|
|
@@ -136,12 +139,13 @@ def _render_scan_ui_if_available(api_client, scanid):
|
|
|
136
139
|
"""Helper function to render scan UI if the method is available."""
|
|
137
140
|
try:
|
|
138
141
|
# This would be the equivalent of the original _render_scan_ui method
|
|
139
|
-
|
|
142
|
+
_ = api_client.get(f"/scan/{scanid}")
|
|
140
143
|
# We don't assert here since this is an optional UI test
|
|
141
144
|
except Exception:
|
|
142
145
|
# Silently pass if UI rendering is not available
|
|
143
146
|
pass
|
|
144
147
|
|
|
148
|
+
|
|
145
149
|
def test_subnet_detection(api_client):
|
|
146
150
|
"""
|
|
147
151
|
Test to ensure multi-subnet detection is working
|
|
@@ -155,9 +159,10 @@ def test_subnet_detection(api_client):
|
|
|
155
159
|
subnet: dict = subnets[0]
|
|
156
160
|
assert subnet.get('address_cnt') is not None
|
|
157
161
|
|
|
158
|
-
# Subnet Validation Tests
|
|
162
|
+
# Subnet Validation Tests
|
|
159
163
|
##########################
|
|
160
164
|
|
|
165
|
+
|
|
161
166
|
@pytest.mark.parametrize("subnet,expected_count", [
|
|
162
167
|
# Valid subnets
|
|
163
168
|
('10.0.0.0/24', 254),
|
|
@@ -185,10 +190,11 @@ def test_subnet_validation(api_client, subnet, expected_count):
|
|
|
185
190
|
data: dict = json.loads(response.data)
|
|
186
191
|
assert data.get('count') == expected_count
|
|
187
192
|
assert data.get('msg') is not None
|
|
188
|
-
|
|
193
|
+
|
|
189
194
|
if expected_count == -1:
|
|
190
195
|
assert not data.get('valid')
|
|
191
196
|
|
|
197
|
+
|
|
192
198
|
@pytest.mark.parametrize("arp_supported,expected_in,expected_not_in", [
|
|
193
199
|
(False, 'POKE_THEN_ARP', 'ARP_LOOKUP'),
|
|
194
200
|
(True, 'ARP_LOOKUP', None)
|
|
@@ -201,12 +207,14 @@ def test_default_scan_configs_arp_handling(api_client, arp_supported, expected_i
|
|
|
201
207
|
assert response.status_code == 200
|
|
202
208
|
configs = json.loads(response.data)
|
|
203
209
|
accurate_lookup = configs['accurate']['lookup_type']
|
|
204
|
-
|
|
210
|
+
|
|
205
211
|
assert expected_in in accurate_lookup
|
|
206
212
|
if expected_not_in:
|
|
207
213
|
assert expected_not_in not in accurate_lookup
|
|
208
214
|
|
|
209
215
|
# UI Rendering Helper
|
|
216
|
+
|
|
217
|
+
|
|
210
218
|
def _render_scan_ui_comprehensive(api_client, scanid):
|
|
211
219
|
"""Test comprehensive UI rendering for a scan."""
|
|
212
220
|
uris = [
|
|
@@ -221,8 +229,9 @@ def _render_scan_ui_comprehensive(api_client, scanid):
|
|
|
221
229
|
response = api_client.get(uri)
|
|
222
230
|
assert response.status_code == 200
|
|
223
231
|
|
|
232
|
+
|
|
224
233
|
@pytest.mark.integration
|
|
225
|
-
@pytest.mark.slow
|
|
234
|
+
@pytest.mark.slow
|
|
226
235
|
def test_scan_api_async(api_client, test_scan_config):
|
|
227
236
|
"""
|
|
228
237
|
Test the full scan API lifecycle with progress monitoring
|
|
@@ -230,8 +239,8 @@ def test_scan_api_async(api_client, test_scan_config):
|
|
|
230
239
|
# Create the port list first (since test_scan_config references it)
|
|
231
240
|
sample_port_list = {'80': 'http', '443': 'https'}
|
|
232
241
|
api_client.post('/api/port/list/test_port_list_scan', json=sample_port_list)
|
|
233
|
-
|
|
234
|
-
# Create a new scan
|
|
242
|
+
|
|
243
|
+
# Create a new scan
|
|
235
244
|
response = api_client.post('/api/scan', json=test_scan_config)
|
|
236
245
|
assert response.status_code == 200
|
|
237
246
|
scan_info = json.loads(response.data)
|
|
@@ -243,20 +252,20 @@ def test_scan_api_async(api_client, test_scan_config):
|
|
|
243
252
|
percent_complete = 0
|
|
244
253
|
max_iterations = 30 # Safety limit
|
|
245
254
|
iteration = 0
|
|
246
|
-
|
|
255
|
+
|
|
247
256
|
while percent_complete < 100 and iteration < max_iterations:
|
|
248
257
|
# Get scan summary
|
|
249
258
|
response = api_client.get(f'/api/scan/{scan_id}/summary')
|
|
250
259
|
assert response.status_code == 200
|
|
251
260
|
summary = json.loads(response.data)
|
|
252
261
|
assert summary['running'] or summary['stage'] == 'complete'
|
|
253
|
-
|
|
262
|
+
|
|
254
263
|
percent_complete = summary['percent_complete']
|
|
255
264
|
assert 0 <= percent_complete <= 100
|
|
256
|
-
|
|
265
|
+
|
|
257
266
|
# Test UI rendering during scan
|
|
258
267
|
_render_scan_ui_if_available(api_client, scan_id)
|
|
259
|
-
|
|
268
|
+
|
|
260
269
|
if percent_complete < 100:
|
|
261
270
|
time.sleep(2)
|
|
262
271
|
iteration += 1
|
|
@@ -270,5 +279,3 @@ def test_scan_api_async(api_client, test_scan_config):
|
|
|
270
279
|
devices = summary['devices']
|
|
271
280
|
assert devices['scanned'] == devices['total']
|
|
272
281
|
assert devices['alive'] > 0
|
|
273
|
-
|
|
274
|
-
|
|
@@ -3,9 +3,10 @@
|
|
|
3
3
|
import logging
|
|
4
4
|
import threading
|
|
5
5
|
import time
|
|
6
|
-
import pytest
|
|
7
6
|
from concurrent.futures import ThreadPoolExecutor, as_completed
|
|
8
7
|
|
|
8
|
+
import pytest
|
|
9
|
+
|
|
9
10
|
from lanscape.core.decorators import run_once, job_tracker, JobStats
|
|
10
11
|
|
|
11
12
|
|
|
@@ -311,15 +312,15 @@ def test_job_tracker_multiple_different_functions():
|
|
|
311
312
|
])
|
|
312
313
|
def test_job_tracker_parametrized_function_calls(function_name, expected_calls):
|
|
313
314
|
"""Test parametrized validation of function call counts."""
|
|
314
|
-
|
|
315
|
+
|
|
315
316
|
@job_tracker
|
|
316
317
|
def function_a():
|
|
317
318
|
return "a"
|
|
318
|
-
|
|
319
|
-
@job_tracker
|
|
319
|
+
|
|
320
|
+
@job_tracker
|
|
320
321
|
def function_b():
|
|
321
322
|
return "b"
|
|
322
|
-
|
|
323
|
+
|
|
323
324
|
# Execute functions based on expected calls
|
|
324
325
|
if function_name == "function_a":
|
|
325
326
|
for _ in range(expected_calls):
|
|
@@ -327,6 +328,6 @@ def test_job_tracker_parametrized_function_calls(function_name, expected_calls):
|
|
|
327
328
|
elif function_name == "function_b":
|
|
328
329
|
for _ in range(expected_calls):
|
|
329
330
|
function_b()
|
|
330
|
-
|
|
331
|
+
|
|
331
332
|
stats = JobStats()
|
|
332
333
|
assert stats.finished[function_name] == expected_calls
|
|
@@ -4,9 +4,10 @@ Verifies functionality related to version checking, resource management,
|
|
|
4
4
|
execution environment detection, and network support features.
|
|
5
5
|
"""
|
|
6
6
|
|
|
7
|
-
import pytest
|
|
8
7
|
from unittest.mock import patch, MagicMock
|
|
9
8
|
|
|
9
|
+
import pytest
|
|
10
|
+
|
|
10
11
|
from lanscape.core.version_manager import lookup_latest_version
|
|
11
12
|
from lanscape.core.app_scope import ResourceManager, is_local_run
|
|
12
13
|
from lanscape.core.net_tools import is_arp_supported
|
|
@@ -27,15 +28,14 @@ def mock_resource_manager():
|
|
|
27
28
|
def test_version_lookup_returns_valid_version():
|
|
28
29
|
"""Test that the version lookup functionality returns a valid version string."""
|
|
29
30
|
version = lookup_latest_version()
|
|
30
|
-
|
|
31
|
+
|
|
31
32
|
assert version is not None
|
|
32
33
|
assert isinstance(version, str)
|
|
33
34
|
# Basic version pattern check (should contain digits and dots)
|
|
34
35
|
assert any(char.isdigit() for char in version)
|
|
35
36
|
|
|
36
37
|
|
|
37
|
-
|
|
38
|
-
def test_version_lookup_return_type(expected_type):
|
|
38
|
+
def test_version_lookup_return_type():
|
|
39
39
|
"""Test version lookup returns expected type."""
|
|
40
40
|
version = lookup_latest_version()
|
|
41
41
|
if version is not None:
|
|
@@ -52,7 +52,7 @@ def test_resource_manager_lists_ports():
|
|
|
52
52
|
"""Test the ResourceManager can list port resources."""
|
|
53
53
|
ports = ResourceManager('ports')
|
|
54
54
|
port_list = ports.list()
|
|
55
|
-
|
|
55
|
+
|
|
56
56
|
assert len(port_list) > 0
|
|
57
57
|
assert isinstance(port_list, list)
|
|
58
58
|
|
|
@@ -61,7 +61,7 @@ def test_resource_manager_retrieves_mac_database():
|
|
|
61
61
|
"""Test ResourceManager can retrieve MAC address database."""
|
|
62
62
|
mac = ResourceManager('mac_addresses')
|
|
63
63
|
mac_list = mac.get('mac_db.json')
|
|
64
|
-
|
|
64
|
+
|
|
65
65
|
assert mac_list is not None
|
|
66
66
|
|
|
67
67
|
|
|
@@ -72,7 +72,7 @@ def test_resource_manager_retrieves_mac_database():
|
|
|
72
72
|
def test_resource_manager_methods(resource_type, expected_method):
|
|
73
73
|
"""Test ResourceManager has required methods for different resource types."""
|
|
74
74
|
manager = ResourceManager(resource_type)
|
|
75
|
-
|
|
75
|
+
|
|
76
76
|
assert hasattr(manager, expected_method)
|
|
77
77
|
assert callable(getattr(manager, expected_method))
|
|
78
78
|
|
|
@@ -81,7 +81,7 @@ def test_resource_manager_with_mock(mock_resource_manager):
|
|
|
81
81
|
"""Test ResourceManager behavior with mocked dependencies."""
|
|
82
82
|
mock_resource_manager.list.return_value = ['port1', 'port2']
|
|
83
83
|
mock_resource_manager.get.return_value = {'test': 'data'}
|
|
84
|
-
|
|
84
|
+
|
|
85
85
|
# Test the mock behavior
|
|
86
86
|
assert mock_resource_manager.list() == ['port1', 'port2']
|
|
87
87
|
assert mock_resource_manager.get('test') == {'test': 'data'}
|
|
@@ -93,7 +93,7 @@ def test_resource_manager_with_mock(mock_resource_manager):
|
|
|
93
93
|
def test_local_run_detection():
|
|
94
94
|
"""Test that the app correctly identifies it's running in a local environment."""
|
|
95
95
|
result = is_local_run()
|
|
96
|
-
|
|
96
|
+
|
|
97
97
|
assert isinstance(result, bool)
|
|
98
98
|
assert result is True # Should be True in test environment
|
|
99
99
|
|
|
@@ -101,17 +101,17 @@ def test_local_run_detection():
|
|
|
101
101
|
def test_arp_support_detection():
|
|
102
102
|
"""Test that ARP support detection returns a valid boolean value."""
|
|
103
103
|
arp_supported = is_arp_supported()
|
|
104
|
-
|
|
104
|
+
|
|
105
105
|
assert isinstance(arp_supported, bool)
|
|
106
106
|
assert arp_supported in [True, False]
|
|
107
107
|
|
|
108
108
|
|
|
109
109
|
@pytest.mark.parametrize("platform,expected", [
|
|
110
110
|
("win32", True), # Windows typically supports ARP
|
|
111
|
-
("linux", True), # Linux typically supports ARP
|
|
111
|
+
("linux", True), # Linux typically supports ARP
|
|
112
112
|
("darwin", True), # macOS typically supports ARP
|
|
113
113
|
])
|
|
114
|
-
def test_arp_support_by_platform(platform, expected):
|
|
114
|
+
def test_arp_support_by_platform(platform, expected): # pylint: disable=unused-argument
|
|
115
115
|
"""Test ARP support expectations by platform (informational test)."""
|
|
116
116
|
with patch('sys.platform', platform):
|
|
117
117
|
# This is more of an informational test about expectations
|
|
@@ -128,20 +128,20 @@ def test_environment_integration():
|
|
|
128
128
|
"""Integration test verifying all environment components work together."""
|
|
129
129
|
# Test version lookup
|
|
130
130
|
version = lookup_latest_version()
|
|
131
|
-
|
|
132
|
-
# Test resource access
|
|
131
|
+
|
|
132
|
+
# Test resource access
|
|
133
133
|
ports_rm = ResourceManager('ports')
|
|
134
|
-
|
|
135
|
-
|
|
134
|
+
_ = ResourceManager('mac_addresses') # MAC database is optional
|
|
135
|
+
|
|
136
136
|
# Test environment detection
|
|
137
137
|
is_local = is_local_run()
|
|
138
138
|
arp_support = is_arp_supported()
|
|
139
|
-
|
|
139
|
+
|
|
140
140
|
# Basic assertions that everything returns reasonable values
|
|
141
141
|
if version is not None:
|
|
142
142
|
assert isinstance(version, str)
|
|
143
|
-
|
|
143
|
+
|
|
144
144
|
assert len(ports_rm.list()) >= 0
|
|
145
|
-
|
|
146
|
-
assert isinstance(is_local, bool)
|
|
145
|
+
# MAC DB is optional, don't assert presence
|
|
146
|
+
assert isinstance(is_local, bool)
|
|
147
147
|
assert isinstance(arp_support, bool)
|
|
@@ -56,6 +56,7 @@ def test_scan_config():
|
|
|
56
56
|
assert cfg2.arp_config.timeout == arp_timeout
|
|
57
57
|
assert cfg2.lookup_type == [ScanType.POKE_THEN_ARP]
|
|
58
58
|
|
|
59
|
+
|
|
59
60
|
@pytest.mark.integration
|
|
60
61
|
@pytest.mark.slow
|
|
61
62
|
def test_scan(scan_manager):
|
|
@@ -65,7 +66,7 @@ def test_scan(scan_manager):
|
|
|
65
66
|
"""
|
|
66
67
|
subnet = smart_select_primary_subnet()
|
|
67
68
|
assert subnet is not None
|
|
68
|
-
|
|
69
|
+
|
|
69
70
|
cfg = ScanConfig(
|
|
70
71
|
subnet=right_size_subnet(subnet),
|
|
71
72
|
t_multiplier=1.0,
|
|
@@ -5,12 +5,12 @@ Tests include log file creation, CLI logging settings, and runtime arguments for
|
|
|
5
5
|
|
|
6
6
|
import logging
|
|
7
7
|
import os
|
|
8
|
+
import shutil
|
|
8
9
|
import tempfile
|
|
9
|
-
import pytest
|
|
10
10
|
from logging.handlers import RotatingFileHandler
|
|
11
11
|
from unittest.mock import patch
|
|
12
12
|
|
|
13
|
-
|
|
13
|
+
import pytest
|
|
14
14
|
import click
|
|
15
15
|
|
|
16
16
|
from lanscape.core.logger import configure_logging
|
|
@@ -21,7 +21,7 @@ from lanscape.core.runtime_args import parse_args
|
|
|
21
21
|
def logging_cleanup():
|
|
22
22
|
"""Clean up logging after test (manual use)."""
|
|
23
23
|
yield
|
|
24
|
-
|
|
24
|
+
|
|
25
25
|
# Cleanup after test
|
|
26
26
|
root = logging.getLogger()
|
|
27
27
|
for handler in root.handlers[:]:
|
|
@@ -39,52 +39,53 @@ def test_configure_logging_writes_file():
|
|
|
39
39
|
root_logger = logging.getLogger()
|
|
40
40
|
original_handlers = root_logger.handlers[:]
|
|
41
41
|
root_logger.handlers.clear()
|
|
42
|
-
|
|
42
|
+
|
|
43
43
|
tmpdir = tempfile.mkdtemp()
|
|
44
44
|
try:
|
|
45
45
|
logfile = os.path.join(tmpdir, 'test.log')
|
|
46
46
|
configure_logging('INFO', logfile, flask_logging=True)
|
|
47
47
|
logging.getLogger('test').info('hello file')
|
|
48
|
-
|
|
48
|
+
|
|
49
49
|
# Flush all handlers to ensure content is written
|
|
50
50
|
for handler in logging.getLogger().handlers:
|
|
51
51
|
handler.flush()
|
|
52
|
-
|
|
52
|
+
|
|
53
53
|
# Read the file contents
|
|
54
54
|
with open(logfile, 'r', encoding='utf-8') as fh:
|
|
55
55
|
contents = fh.read()
|
|
56
|
-
|
|
56
|
+
|
|
57
57
|
# Clean up handlers to release file locks
|
|
58
58
|
for handler in logging.getLogger().handlers[:]:
|
|
59
59
|
if hasattr(handler, 'close'):
|
|
60
60
|
handler.close()
|
|
61
61
|
logging.getLogger().handlers.clear()
|
|
62
|
-
|
|
62
|
+
|
|
63
63
|
assert 'hello file' in contents
|
|
64
|
-
|
|
64
|
+
|
|
65
65
|
finally:
|
|
66
66
|
# Restore original handlers
|
|
67
67
|
root_logger.handlers = original_handlers
|
|
68
|
-
|
|
68
|
+
|
|
69
69
|
# Clean up the temp directory
|
|
70
|
-
import shutil
|
|
71
70
|
try:
|
|
72
71
|
shutil.rmtree(tmpdir)
|
|
73
72
|
except PermissionError:
|
|
74
73
|
# On Windows, sometimes files are still locked, ignore cleanup errors
|
|
75
74
|
pass
|
|
76
|
-
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def test_configure_logging_without_file(logging_cleanup): # pylint: disable=unused-argument
|
|
77
78
|
"""Test that no file handlers are created when no log file is specified."""
|
|
78
79
|
configure_logging('INFO', None, flask_logging=True)
|
|
79
80
|
root_handlers = logging.getLogger().handlers
|
|
80
81
|
assert all(not isinstance(h, RotatingFileHandler) for h in root_handlers)
|
|
81
82
|
|
|
82
83
|
|
|
83
|
-
def test_disable_flask_logging_overrides_click(logging_cleanup):
|
|
84
|
+
def test_disable_flask_logging_overrides_click(logging_cleanup): # pylint: disable=unused-argument
|
|
84
85
|
"""Test that disabling Flask logging properly overrides click echo functions."""
|
|
85
86
|
original_click_echo = click.echo
|
|
86
87
|
original_click_secho = click.secho
|
|
87
|
-
|
|
88
|
+
|
|
88
89
|
configure_logging('INFO', None, flask_logging=False)
|
|
89
90
|
assert click.echo != original_click_echo
|
|
90
91
|
assert click.secho != original_click_secho
|
|
@@ -3,9 +3,10 @@ Tests for port scanning functionality including the new PortScanConfig
|
|
|
3
3
|
retry logic and timeout enforcement.
|
|
4
4
|
"""
|
|
5
5
|
|
|
6
|
-
import pytest
|
|
7
|
-
from unittest.mock import patch, MagicMock
|
|
8
6
|
from time import time
|
|
7
|
+
from unittest.mock import patch, MagicMock
|
|
8
|
+
|
|
9
|
+
import pytest
|
|
9
10
|
|
|
10
11
|
from lanscape.core.net_tools import Device
|
|
11
12
|
from lanscape.core.scan_config import PortScanConfig
|
|
@@ -35,6 +36,7 @@ def retry_port_config():
|
|
|
35
36
|
# PortScanConfig Tests
|
|
36
37
|
######################
|
|
37
38
|
|
|
39
|
+
|
|
38
40
|
def test_port_scan_config_defaults():
|
|
39
41
|
"""Test PortScanConfig default values."""
|
|
40
42
|
config = PortScanConfig()
|
|
@@ -80,6 +82,7 @@ def test_port_scan_config_serialization():
|
|
|
80
82
|
# Device Port Testing
|
|
81
83
|
####################
|
|
82
84
|
|
|
85
|
+
|
|
83
86
|
def test_device_test_port_with_default_config(test_device, default_port_config):
|
|
84
87
|
"""Test Device.test_port with default PortScanConfig."""
|
|
85
88
|
# Test with a port that should be closed
|
|
@@ -98,6 +101,7 @@ def test_device_test_port_without_config(test_device):
|
|
|
98
101
|
assert isinstance(result, bool)
|
|
99
102
|
assert test_device.ports_scanned == initial_count + 1
|
|
100
103
|
|
|
104
|
+
|
|
101
105
|
@patch('socket.socket')
|
|
102
106
|
def test_device_test_port_with_retries(mock_socket_class, test_device):
|
|
103
107
|
"""Test Device.test_port retry mechanism."""
|
|
@@ -167,6 +171,7 @@ def test_device_test_port_exception_handling(mock_socket_class, test_device):
|
|
|
167
171
|
# Timeout and Configuration Tests
|
|
168
172
|
##################################
|
|
169
173
|
|
|
174
|
+
|
|
170
175
|
@pytest.mark.parametrize("timeout,retries,expected_enforcer_timeout", [
|
|
171
176
|
(1.0, 0, 1.5), # 1.0 * (0 + 1) * 1.5 = 1.5
|
|
172
177
|
(2.0, 2, 9.0), # 2.0 * (2 + 1) * 1.5 = 9.0
|
|
@@ -176,11 +181,12 @@ def test_device_test_port_exception_handling(mock_socket_class, test_device):
|
|
|
176
181
|
def test_timeout_enforcer_calculation(timeout, retries, expected_enforcer_timeout):
|
|
177
182
|
"""Test that timeout enforcer uses correct formula."""
|
|
178
183
|
config = PortScanConfig(timeout=timeout, retries=retries, retry_delay=0.1)
|
|
179
|
-
|
|
184
|
+
|
|
180
185
|
# Formula: timeout * (retries + 1) * 1.5
|
|
181
186
|
calculated_timeout = config.timeout * (config.retries + 1) * 1.5
|
|
182
187
|
assert calculated_timeout == expected_enforcer_timeout
|
|
183
188
|
|
|
189
|
+
|
|
184
190
|
def test_device_ports_scanned_counter(test_device, default_port_config):
|
|
185
191
|
"""Test that ports_scanned counter is properly incremented."""
|
|
186
192
|
initial_count = test_device.ports_scanned
|
|
@@ -3,18 +3,17 @@ Dedicated tests for service scanning functionality.
|
|
|
3
3
|
Tests the service_scan module including async probing, service identification,
|
|
4
4
|
and configuration handling.
|
|
5
5
|
"""
|
|
6
|
-
import logging
|
|
7
6
|
import asyncio
|
|
8
|
-
import pytest
|
|
9
7
|
from unittest.mock import patch, AsyncMock, MagicMock
|
|
10
8
|
|
|
9
|
+
import pytest
|
|
10
|
+
|
|
11
11
|
from lanscape.core.service_scan import (
|
|
12
12
|
scan_service,
|
|
13
13
|
get_port_probes,
|
|
14
14
|
_try_probe,
|
|
15
15
|
_multi_probe_generic,
|
|
16
|
-
PRINTER_PORTS
|
|
17
|
-
asyncio_logger_suppression
|
|
16
|
+
PRINTER_PORTS
|
|
18
17
|
)
|
|
19
18
|
from lanscape.core.scan_config import ServiceScanConfig, ServiceScanStrategy
|
|
20
19
|
|
|
@@ -50,6 +49,7 @@ def aggressive_config():
|
|
|
50
49
|
# Strategy and Probe Generation Tests
|
|
51
50
|
####################################
|
|
52
51
|
|
|
52
|
+
|
|
53
53
|
def test_service_scan_strategy_enum():
|
|
54
54
|
"""Test ServiceScanStrategy enum values."""
|
|
55
55
|
assert ServiceScanStrategy.LAZY.value == 'LAZY'
|
|
@@ -73,6 +73,7 @@ def test_get_port_probes_lazy_strategy():
|
|
|
73
73
|
http_probes = [p for p in probes if p and b"HTTP" in p]
|
|
74
74
|
assert len(http_probes) > 0
|
|
75
75
|
|
|
76
|
+
|
|
76
77
|
@pytest.mark.parametrize("port", [22, 80, 443])
|
|
77
78
|
def test_get_port_probes_basic_strategy(port):
|
|
78
79
|
"""Test probe generation for BASIC strategy."""
|
|
@@ -152,7 +153,7 @@ def test_concurrent_probe_limits():
|
|
|
152
153
|
assert isinstance(result2, str)
|
|
153
154
|
|
|
154
155
|
|
|
155
|
-
# Async Probe Tests
|
|
156
|
+
# Async Probe Tests
|
|
156
157
|
##################
|
|
157
158
|
|
|
158
159
|
def test_try_probe_success():
|
|
@@ -211,19 +212,6 @@ def test_multi_probe_generic_no_response():
|
|
|
211
212
|
asyncio.run(run_test())
|
|
212
213
|
|
|
213
214
|
|
|
214
|
-
# Logger and Integration Tests
|
|
215
|
-
#############################
|
|
216
|
-
|
|
217
|
-
def test_asyncio_logger_suppression():
|
|
218
|
-
"""Test that asyncio logger suppression works."""
|
|
219
|
-
# This should not raise any exceptions
|
|
220
|
-
asyncio_logger_suppression()
|
|
221
|
-
|
|
222
|
-
# Verify that asyncio logger level was changed
|
|
223
|
-
asyncio_logger = logging.getLogger("asyncio")
|
|
224
|
-
assert asyncio_logger.level >= logging.WARNING
|
|
225
|
-
|
|
226
|
-
|
|
227
215
|
@pytest.mark.integration
|
|
228
216
|
def test_service_scan_integration():
|
|
229
217
|
"""Integration test for full service scanning workflow."""
|
|
@@ -280,4 +268,3 @@ def test_probe_payload_types():
|
|
|
280
268
|
|
|
281
269
|
assert has_none, "Should include None for banner grab"
|
|
282
270
|
assert has_bytes, "Should include bytes payloads"
|
|
283
|
-
|
|
@@ -3,9 +3,10 @@ Unit tests for various utility modules in the LANscape project.
|
|
|
3
3
|
Tests include IP parsing, port management, and decorator functionality.
|
|
4
4
|
"""
|
|
5
5
|
|
|
6
|
+
import ipaddress
|
|
6
7
|
import time
|
|
8
|
+
|
|
7
9
|
import pytest
|
|
8
|
-
import ipaddress
|
|
9
10
|
|
|
10
11
|
from lanscape.core.ip_parser import parse_ip_input
|
|
11
12
|
from lanscape.core.errors import SubnetTooLargeError
|
|
@@ -37,7 +38,7 @@ def test_parse_cidr_specific():
|
|
|
37
38
|
def test_parse_range_length_and_bounds():
|
|
38
39
|
"""Test explicit IP range parsing validates length and boundaries."""
|
|
39
40
|
ips = parse_ip_input('10.0.0.1-10.0.0.3')
|
|
40
|
-
|
|
41
|
+
|
|
41
42
|
assert len(ips) == 3
|
|
42
43
|
assert str(ips[0]) == '10.0.0.1'
|
|
43
44
|
assert str(ips[-1]) == '10.0.0.3'
|
|
@@ -53,7 +54,7 @@ def test_parse_mixed_format_comprehensive():
|
|
|
53
54
|
"""Test parsing a comprehensive mix of CIDR, range, and individual IP formats."""
|
|
54
55
|
ip_input = "10.0.0.1/30, 10.0.0.10-10.0.0.12, 10.0.0.20-22, 10.0.0.50"
|
|
55
56
|
result = ip_parser.parse_ip_input(ip_input)
|
|
56
|
-
|
|
57
|
+
|
|
57
58
|
expected = [
|
|
58
59
|
ipaddress.IPv4Address("10.0.0.1"),
|
|
59
60
|
ipaddress.IPv4Address("10.0.0.2"),
|
|
@@ -65,7 +66,7 @@ def test_parse_mixed_format_comprehensive():
|
|
|
65
66
|
ipaddress.IPv4Address("10.0.0.22"),
|
|
66
67
|
ipaddress.IPv4Address("10.0.0.50"),
|
|
67
68
|
]
|
|
68
|
-
|
|
69
|
+
|
|
69
70
|
assert result == expected
|
|
70
71
|
|
|
71
72
|
|
|
@@ -106,35 +107,35 @@ def test_port_manager_allows_empty_service_name(port_manager):
|
|
|
106
107
|
|
|
107
108
|
def test_timeout_enforcer_no_raise():
|
|
108
109
|
"""Test timeout_enforcer with raise_on_timeout=False returns None on timeout."""
|
|
109
|
-
|
|
110
|
+
|
|
110
111
|
@timeout_enforcer(0.1, raise_on_timeout=False)
|
|
111
112
|
def slow_function():
|
|
112
113
|
time.sleep(0.5)
|
|
113
114
|
return "should_not_return"
|
|
114
|
-
|
|
115
|
+
|
|
115
116
|
result = slow_function()
|
|
116
117
|
assert result is None
|
|
117
118
|
|
|
118
119
|
|
|
119
120
|
def test_timeout_enforcer_with_raise():
|
|
120
121
|
"""Test timeout_enforcer with raise_on_timeout=True raises TimeoutError."""
|
|
121
|
-
|
|
122
|
+
|
|
122
123
|
@timeout_enforcer(0.1, raise_on_timeout=True)
|
|
123
124
|
def slow_function():
|
|
124
125
|
time.sleep(0.5)
|
|
125
126
|
return "should_not_return"
|
|
126
|
-
|
|
127
|
+
|
|
127
128
|
with pytest.raises(TimeoutError):
|
|
128
129
|
slow_function()
|
|
129
130
|
|
|
130
131
|
|
|
131
132
|
def test_timeout_enforcer_fast_function():
|
|
132
133
|
"""Test timeout_enforcer allows fast functions to complete normally."""
|
|
133
|
-
|
|
134
|
+
|
|
134
135
|
@timeout_enforcer(1.0, raise_on_timeout=True)
|
|
135
136
|
def fast_function():
|
|
136
137
|
return "completed"
|
|
137
|
-
|
|
138
|
+
|
|
138
139
|
result = fast_function()
|
|
139
140
|
assert result == "completed"
|
|
140
141
|
|
|
@@ -145,12 +146,12 @@ def test_timeout_enforcer_fast_function():
|
|
|
145
146
|
])
|
|
146
147
|
def test_timeout_enforcer_parametrized(timeout, raise_flag, expected_exception):
|
|
147
148
|
"""Test timeout_enforcer with different timeout values and raise settings."""
|
|
148
|
-
|
|
149
|
+
|
|
149
150
|
@timeout_enforcer(timeout, raise_on_timeout=raise_flag)
|
|
150
151
|
def slow_function():
|
|
151
152
|
time.sleep(0.2)
|
|
152
153
|
return "done"
|
|
153
|
-
|
|
154
|
+
|
|
154
155
|
if raise_flag:
|
|
155
156
|
with pytest.raises(expected_exception):
|
|
156
157
|
slow_function()
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|