woninet 2.0.1__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.
Files changed (62) hide show
  1. woninet-2.0.1/LICENSE +21 -0
  2. woninet-2.0.1/PKG-INFO +243 -0
  3. woninet-2.0.1/README.md +216 -0
  4. woninet-2.0.1/pyproject.toml +55 -0
  5. woninet-2.0.1/setup.cfg +4 -0
  6. woninet-2.0.1/tests/test_alert_events.py +151 -0
  7. woninet-2.0.1/tests/test_api.py +10 -0
  8. woninet-2.0.1/tests/test_ip_validation.py +9 -0
  9. woninet-2.0.1/tests/test_storage.py +24 -0
  10. woninet-2.0.1/woninet/__init__.py +2 -0
  11. woninet-2.0.1/woninet/__main__.py +4 -0
  12. woninet-2.0.1/woninet/core/__init__.py +2 -0
  13. woninet-2.0.1/woninet/core/alerts.py +251 -0
  14. woninet-2.0.1/woninet/core/collectors.py +303 -0
  15. woninet-2.0.1/woninet/core/engine.py +353 -0
  16. woninet-2.0.1/woninet/core/graph.py +63 -0
  17. woninet-2.0.1/woninet/core/manual_ip_enumerator.py +33 -0
  18. woninet-2.0.1/woninet/core/models.py +124 -0
  19. woninet-2.0.1/woninet/core/storage.py +163 -0
  20. woninet-2.0.1/woninet/core/subnet_enumerator.py +32 -0
  21. woninet-2.0.1/woninet/database/__init__.py +2 -0
  22. woninet-2.0.1/woninet/database/base.py +9 -0
  23. woninet-2.0.1/woninet/database/engine.py +44 -0
  24. woninet-2.0.1/woninet/database/repositories/__init__.py +2 -0
  25. woninet-2.0.1/woninet/database/repositories/alert_event_repository.py +69 -0
  26. woninet-2.0.1/woninet/database/repositories/alert_state_repository.py +81 -0
  27. woninet-2.0.1/woninet/database/repositories/device_repository.py +105 -0
  28. woninet-2.0.1/woninet/database/repositories/metric_repository.py +75 -0
  29. woninet-2.0.1/woninet/database/tables.py +89 -0
  30. woninet-2.0.1/woninet/logging.yaml +49 -0
  31. woninet-2.0.1/woninet/main.py +225 -0
  32. woninet-2.0.1/woninet/server/__init__.py +2 -0
  33. woninet-2.0.1/woninet/server/app.py +101 -0
  34. woninet-2.0.1/woninet/server/dependencies.py +25 -0
  35. woninet-2.0.1/woninet/server/routes/__init__.py +2 -0
  36. woninet-2.0.1/woninet/server/routes/api/__init__.py +0 -0
  37. woninet-2.0.1/woninet/server/routes/api/devices.py +43 -0
  38. woninet-2.0.1/woninet/server/routes/api/stats.py +34 -0
  39. woninet-2.0.1/woninet/server/routes/page/__init__.py +0 -0
  40. woninet-2.0.1/woninet/server/routes/page/devices.py +28 -0
  41. woninet-2.0.1/woninet/server/static/css/404.css +164 -0
  42. woninet-2.0.1/woninet/server/static/css/style.css +368 -0
  43. woninet-2.0.1/woninet/server/static/js/app.js +242 -0
  44. woninet-2.0.1/woninet/server/static/js/device_info.js +95 -0
  45. woninet-2.0.1/woninet/server/static/swagger-ui/favicon-32x32.png +0 -0
  46. woninet-2.0.1/woninet/server/static/swagger-ui/swagger-ui-bundle.js +2 -0
  47. woninet-2.0.1/woninet/server/static/swagger-ui/swagger-ui.css +3 -0
  48. woninet-2.0.1/woninet/server/templates/404.html +76 -0
  49. woninet-2.0.1/woninet/server/templates/dashboard.html +118 -0
  50. woninet-2.0.1/woninet/server/templates/device_info.html +93 -0
  51. woninet-2.0.1/woninet/utilities/__init__.py +2 -0
  52. woninet-2.0.1/woninet/utilities/arguments.py +94 -0
  53. woninet-2.0.1/woninet/utilities/cli_parser.py +90 -0
  54. woninet-2.0.1/woninet/utilities/detect_ip_range.py +21 -0
  55. woninet-2.0.1/woninet/utilities/ip_validator.py +39 -0
  56. woninet-2.0.1/woninet/utilities/logger.py +97 -0
  57. woninet-2.0.1/woninet.egg-info/PKG-INFO +243 -0
  58. woninet-2.0.1/woninet.egg-info/SOURCES.txt +60 -0
  59. woninet-2.0.1/woninet.egg-info/dependency_links.txt +1 -0
  60. woninet-2.0.1/woninet.egg-info/entry_points.txt +2 -0
  61. woninet-2.0.1/woninet.egg-info/requires.txt +11 -0
  62. woninet-2.0.1/woninet.egg-info/top_level.txt +1 -0
woninet-2.0.1/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Alireza Teymuri
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
woninet-2.0.1/PKG-INFO ADDED
@@ -0,0 +1,243 @@
1
+ Metadata-Version: 2.4
2
+ Name: woninet
3
+ Version: 2.0.1
4
+ Summary: A Python-based ARP+ICMP network monitoring tool
5
+ Author-email: Alireza Teymuri <teymuri.alireza98@gmail.com>
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://github.com/teymuri-alireza/woninet
8
+ Project-URL: Documentation, https://github.com/teymuri-alireza/woninet/tree/main/docs
9
+ Project-URL: Repository, https://github.com/teymuri-alireza/woninet
10
+ Project-URL: Issues, https://github.com/teymuri-alireza/woninet/issues
11
+ Project-URL: Changelog, https://github.com/teymuri-alireza/woninet/blob/main/CHANGELOG.md
12
+ Keywords: network,network-monitoring,icmp,arp
13
+ Requires-Python: >=3.12
14
+ Description-Content-Type: text/markdown
15
+ License-File: LICENSE
16
+ Requires-Dist: icmplib<3.1,>=3.0.4
17
+ Requires-Dist: fastapi<0.137,>=0.136.0
18
+ Requires-Dist: Jinja2<3.2,>=3.1.6
19
+ Requires-Dist: uvicorn<0.45,>=0.44.0
20
+ Requires-Dist: PyYAML<6.1,>=6.0.3
21
+ Requires-Dist: SQLAlchemy<2.1,>=2.0.46
22
+ Requires-Dist: matplotlib<3.12,>=3.11.0
23
+ Provides-Extra: test
24
+ Requires-Dist: pytest<9.2,>=9.1.1; extra == "test"
25
+ Requires-Dist: httpx<0.29,>=0.28.1; extra == "test"
26
+ Dynamic: license-file
27
+
28
+ # woninet
29
+
30
+ A local network monitoring system.
31
+
32
+ **woninet** continuously monitors devices on a local network using ARP
33
+ and ICMP probes. It enumerates candidate IP addresses in a /24 subnet,
34
+ determines device availability, measures latency and packet loss, stores
35
+ metrics in SQLite, and evaluates configurable alert rules in real time.
36
+
37
+ **Note: This script requires sudo privileges.**
38
+
39
+ ## Features
40
+
41
+ - **ARP + ICMP Host Detection** (Distinguishes real devices from nonexistent
42
+ IPs and filters out Wi‑Fi ARP‑delay noise.)
43
+
44
+ - **Alert Engine** (Evaluates real‑time alert rules (e.g., latency thresholds))
45
+
46
+ - **Threaded Collectors**
47
+
48
+ - **SQLite database** (for devices, recorded metrics, alert states, and alert events.)
49
+
50
+ - **Positional Argument for easier access to the database from the CLI**
51
+
52
+ - **Detailed Logging**
53
+
54
+ ## Future Plans
55
+
56
+ - Add ARP table caching to further reduce Wi‑Fi latency noise.
57
+ - Accept MAC addresses as command‑line arguments.
58
+ - Fetch MAC address from database for offline devices.
59
+ - Use ARP noise limit as device state for styling in front-end.
60
+ - Add CLI argument to accept target IP addresses from a file.
61
+
62
+ ## Known Issues
63
+
64
+ - Monitoring Scope Limited to /24 Subnet
65
+
66
+ ## How To Run
67
+
68
+ **Note:** *woninet* is under active development.
69
+
70
+ ## Installation
71
+
72
+ Install from PyPI:
73
+
74
+ ```shell
75
+ pip install woninet
76
+ ```
77
+
78
+ Then run:
79
+
80
+ ```shell
81
+ woninet --help
82
+ ```
83
+
84
+ ## Installation from Source
85
+
86
+ ### 1. Clone the repository
87
+
88
+ ```shell
89
+ git clone https://github.com/teymuri-alireza/woninet
90
+ cd woninet
91
+ ```
92
+
93
+ ### 2. Create and activate a virtual environment:
94
+
95
+ **Note:** Create the virtual environment outside the project directory to avoid
96
+ `Multiple top-level packages discovered in a flat-layout` errors.
97
+
98
+ **Unix/Linux note:** The `--copies` flag forces Python to copy the interpreter
99
+ instead of using a symbolic link. This is required because you will apply Linux
100
+ capabilities directly to the Python binary in the virtual environment.
101
+
102
+ #### Linux/macOS
103
+
104
+ ```shell
105
+ python3 -m venv --copies ../venv
106
+ source ../venv/bin/activate
107
+ ```
108
+
109
+ #### Windows (PowerShell)
110
+
111
+ ```shell
112
+ py -m venv ..\venv
113
+ ..\venv\Scripts\Activate.ps1
114
+ ```
115
+
116
+ ### 3. Install woninet locally:
117
+
118
+ ```shell
119
+ pip3 install .
120
+ ```
121
+
122
+ ### 4. Run woninet
123
+
124
+ **Unix/Linux only:** To allow raw ICMP without running as root, grant the required
125
+ capability to the Python interpreter inside the virtual environment:
126
+
127
+ ```shell
128
+ sudo setcap cap_net_raw=eip ../venv/bin/python3
129
+ ```
130
+
131
+ Finally, run woninet normally:
132
+
133
+ ```shell
134
+ woninet --version
135
+ ```
136
+
137
+ **Alternative (not recommended):** You can run woninet with sudo instead of using
138
+ setcap, but this is discouraged for security reasons:
139
+
140
+ ```shell
141
+ sudo ../venv/bin/python3 -m woninet
142
+ ```
143
+
144
+ ## Configuration
145
+
146
+ You can define alert rules in a JSON file. Rename the example file at
147
+ `woninet/config.example.json` to `config.json`. The application will load the alert
148
+ rules from that file at startup.
149
+
150
+ ### Example:
151
+
152
+ ```json
153
+ {
154
+ "monitoring": {
155
+ "arp_noise_limit": 300.0,
156
+ "max_workers": 4
157
+ },
158
+ "database": "woninet.db",
159
+ "target_ip_list": [
160
+ "192.168.1.1-100"
161
+ // or
162
+ "192.168.1.1", "192.168.1.10", "192.168.1.20"
163
+ ],
164
+ "alert_rules":[
165
+ {
166
+ "metric": "latency",
167
+ "threshold": 100,
168
+ "consecutive_checks": 3
169
+ },
170
+ {
171
+ "metric": "packet_loss",
172
+ "threshold": 0.0,
173
+ "consecutive_checks": 1
174
+ }
175
+ ]
176
+ }
177
+ ```
178
+
179
+ ### Explanations:
180
+
181
+ #### Monitoring Settings
182
+
183
+ - **arp_noise_limit**: Latency threshold in milliseconds used to filter ARP
184
+ resolution noise. Set to 0 to disable filtering. Helps eliminate Wi‑Fi latency spikes.
185
+ - **max_workers**: Maximum number of thread workers used to send ICMP pings.
186
+ Higher values may increase system load and latency.
187
+
188
+ #### Database
189
+
190
+ - **database**: The path to the SQLite database file where metrics and alerts are stored.
191
+
192
+ #### Target IP List
193
+
194
+ - A single or range of target IP addresses to scan.
195
+
196
+ #### Alert Rules
197
+
198
+ - **metric**: The metric name to evaluate (`latency` or `packet_loss`)
199
+ - **threshold**: The value that triggers the alert. For latency, this is in milliseconds.
200
+ For packet loss, this is a percentage (0.0-100.0).
201
+ - **consecutive_checks**: The number of consecutive evaluations required before the alert
202
+ state changes. Higher values reduce false positives.
203
+
204
+ #### Notes
205
+
206
+ - **Packet Loss**: Values are in the range [0.0, 100.0]. A value of 100.0 indicates 100%
207
+ packet loss. A value of 0.0 will trigger on any packet loss.
208
+ - **Latency**: All latency thresholds are evaluated in milliseconds.
209
+
210
+ ## Usage
211
+
212
+ This [guide](./docs/usage.md) explains common ways to run *woninet* and how to resolve common issues.
213
+
214
+ ## Testing
215
+
216
+ If you want to run the test suite locally, install the optional test dependencies:
217
+
218
+ ```shell
219
+ pip install -e ".[test]"
220
+ ```
221
+
222
+ Then run:
223
+
224
+ ```shell
225
+ pytest
226
+ ```
227
+
228
+ The test suite currently focuses on API behavior and core logic, and it uses pytest for automated verification.
229
+
230
+ ## Requirements
231
+
232
+ - Python >= 3.12
233
+ - Root privileges
234
+
235
+ ## Documentation
236
+
237
+ if you have any question or need clarification, check the [documentation](./docs/documentation.md) guide.
238
+
239
+ ## Contribution
240
+
241
+ Thanks for considering contributing to *woninet!*
242
+ To help us maintain code quality, stability, and a predictable release process, please review
243
+ and follow the guidelines in our [Contributing Guide.](./CONTRIBUTING.md)
@@ -0,0 +1,216 @@
1
+ # woninet
2
+
3
+ A local network monitoring system.
4
+
5
+ **woninet** continuously monitors devices on a local network using ARP
6
+ and ICMP probes. It enumerates candidate IP addresses in a /24 subnet,
7
+ determines device availability, measures latency and packet loss, stores
8
+ metrics in SQLite, and evaluates configurable alert rules in real time.
9
+
10
+ **Note: This script requires sudo privileges.**
11
+
12
+ ## Features
13
+
14
+ - **ARP + ICMP Host Detection** (Distinguishes real devices from nonexistent
15
+ IPs and filters out Wi‑Fi ARP‑delay noise.)
16
+
17
+ - **Alert Engine** (Evaluates real‑time alert rules (e.g., latency thresholds))
18
+
19
+ - **Threaded Collectors**
20
+
21
+ - **SQLite database** (for devices, recorded metrics, alert states, and alert events.)
22
+
23
+ - **Positional Argument for easier access to the database from the CLI**
24
+
25
+ - **Detailed Logging**
26
+
27
+ ## Future Plans
28
+
29
+ - Add ARP table caching to further reduce Wi‑Fi latency noise.
30
+ - Accept MAC addresses as command‑line arguments.
31
+ - Fetch MAC address from database for offline devices.
32
+ - Use ARP noise limit as device state for styling in front-end.
33
+ - Add CLI argument to accept target IP addresses from a file.
34
+
35
+ ## Known Issues
36
+
37
+ - Monitoring Scope Limited to /24 Subnet
38
+
39
+ ## How To Run
40
+
41
+ **Note:** *woninet* is under active development.
42
+
43
+ ## Installation
44
+
45
+ Install from PyPI:
46
+
47
+ ```shell
48
+ pip install woninet
49
+ ```
50
+
51
+ Then run:
52
+
53
+ ```shell
54
+ woninet --help
55
+ ```
56
+
57
+ ## Installation from Source
58
+
59
+ ### 1. Clone the repository
60
+
61
+ ```shell
62
+ git clone https://github.com/teymuri-alireza/woninet
63
+ cd woninet
64
+ ```
65
+
66
+ ### 2. Create and activate a virtual environment:
67
+
68
+ **Note:** Create the virtual environment outside the project directory to avoid
69
+ `Multiple top-level packages discovered in a flat-layout` errors.
70
+
71
+ **Unix/Linux note:** The `--copies` flag forces Python to copy the interpreter
72
+ instead of using a symbolic link. This is required because you will apply Linux
73
+ capabilities directly to the Python binary in the virtual environment.
74
+
75
+ #### Linux/macOS
76
+
77
+ ```shell
78
+ python3 -m venv --copies ../venv
79
+ source ../venv/bin/activate
80
+ ```
81
+
82
+ #### Windows (PowerShell)
83
+
84
+ ```shell
85
+ py -m venv ..\venv
86
+ ..\venv\Scripts\Activate.ps1
87
+ ```
88
+
89
+ ### 3. Install woninet locally:
90
+
91
+ ```shell
92
+ pip3 install .
93
+ ```
94
+
95
+ ### 4. Run woninet
96
+
97
+ **Unix/Linux only:** To allow raw ICMP without running as root, grant the required
98
+ capability to the Python interpreter inside the virtual environment:
99
+
100
+ ```shell
101
+ sudo setcap cap_net_raw=eip ../venv/bin/python3
102
+ ```
103
+
104
+ Finally, run woninet normally:
105
+
106
+ ```shell
107
+ woninet --version
108
+ ```
109
+
110
+ **Alternative (not recommended):** You can run woninet with sudo instead of using
111
+ setcap, but this is discouraged for security reasons:
112
+
113
+ ```shell
114
+ sudo ../venv/bin/python3 -m woninet
115
+ ```
116
+
117
+ ## Configuration
118
+
119
+ You can define alert rules in a JSON file. Rename the example file at
120
+ `woninet/config.example.json` to `config.json`. The application will load the alert
121
+ rules from that file at startup.
122
+
123
+ ### Example:
124
+
125
+ ```json
126
+ {
127
+ "monitoring": {
128
+ "arp_noise_limit": 300.0,
129
+ "max_workers": 4
130
+ },
131
+ "database": "woninet.db",
132
+ "target_ip_list": [
133
+ "192.168.1.1-100"
134
+ // or
135
+ "192.168.1.1", "192.168.1.10", "192.168.1.20"
136
+ ],
137
+ "alert_rules":[
138
+ {
139
+ "metric": "latency",
140
+ "threshold": 100,
141
+ "consecutive_checks": 3
142
+ },
143
+ {
144
+ "metric": "packet_loss",
145
+ "threshold": 0.0,
146
+ "consecutive_checks": 1
147
+ }
148
+ ]
149
+ }
150
+ ```
151
+
152
+ ### Explanations:
153
+
154
+ #### Monitoring Settings
155
+
156
+ - **arp_noise_limit**: Latency threshold in milliseconds used to filter ARP
157
+ resolution noise. Set to 0 to disable filtering. Helps eliminate Wi‑Fi latency spikes.
158
+ - **max_workers**: Maximum number of thread workers used to send ICMP pings.
159
+ Higher values may increase system load and latency.
160
+
161
+ #### Database
162
+
163
+ - **database**: The path to the SQLite database file where metrics and alerts are stored.
164
+
165
+ #### Target IP List
166
+
167
+ - A single or range of target IP addresses to scan.
168
+
169
+ #### Alert Rules
170
+
171
+ - **metric**: The metric name to evaluate (`latency` or `packet_loss`)
172
+ - **threshold**: The value that triggers the alert. For latency, this is in milliseconds.
173
+ For packet loss, this is a percentage (0.0-100.0).
174
+ - **consecutive_checks**: The number of consecutive evaluations required before the alert
175
+ state changes. Higher values reduce false positives.
176
+
177
+ #### Notes
178
+
179
+ - **Packet Loss**: Values are in the range [0.0, 100.0]. A value of 100.0 indicates 100%
180
+ packet loss. A value of 0.0 will trigger on any packet loss.
181
+ - **Latency**: All latency thresholds are evaluated in milliseconds.
182
+
183
+ ## Usage
184
+
185
+ This [guide](./docs/usage.md) explains common ways to run *woninet* and how to resolve common issues.
186
+
187
+ ## Testing
188
+
189
+ If you want to run the test suite locally, install the optional test dependencies:
190
+
191
+ ```shell
192
+ pip install -e ".[test]"
193
+ ```
194
+
195
+ Then run:
196
+
197
+ ```shell
198
+ pytest
199
+ ```
200
+
201
+ The test suite currently focuses on API behavior and core logic, and it uses pytest for automated verification.
202
+
203
+ ## Requirements
204
+
205
+ - Python >= 3.12
206
+ - Root privileges
207
+
208
+ ## Documentation
209
+
210
+ if you have any question or need clarification, check the [documentation](./docs/documentation.md) guide.
211
+
212
+ ## Contribution
213
+
214
+ Thanks for considering contributing to *woninet!*
215
+ To help us maintain code quality, stability, and a predictable release process, please review
216
+ and follow the guidelines in our [Contributing Guide.](./CONTRIBUTING.md)
@@ -0,0 +1,55 @@
1
+ [build-system]
2
+ requires = ["setuptools>=80", "wheel"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "woninet"
7
+ version = "2.0.1"
8
+ description = "A Python-based ARP+ICMP network monitoring tool"
9
+ readme = "README.md"
10
+ requires-python = ">=3.12"
11
+ license = "MIT"
12
+
13
+ authors = [
14
+ { name = "Alireza Teymuri", email = "teymuri.alireza98@gmail.com" }
15
+ ]
16
+
17
+ keywords = [
18
+ "network",
19
+ "network-monitoring",
20
+ "icmp",
21
+ "arp",
22
+ ]
23
+
24
+ dependencies = [
25
+ "icmplib>=3.0.4,<3.1",
26
+ "fastapi>=0.136.0,<0.137",
27
+ "Jinja2>=3.1.6,<3.2",
28
+ "uvicorn>=0.44.0,<0.45",
29
+ "PyYAML>=6.0.3,<6.1",
30
+ "SQLAlchemy>=2.0.46,<2.1",
31
+ "matplotlib>=3.11.0,<3.12",
32
+ ]
33
+
34
+ [tool.setuptools.package-data]
35
+ woninet = [
36
+ "server/static/**/*",
37
+ "server/templates/**/*",
38
+ "logging.yaml",
39
+ ]
40
+
41
+ [project.urls]
42
+ Homepage = "https://github.com/teymuri-alireza/woninet"
43
+ Documentation = "https://github.com/teymuri-alireza/woninet/tree/main/docs"
44
+ Repository = "https://github.com/teymuri-alireza/woninet"
45
+ Issues = "https://github.com/teymuri-alireza/woninet/issues"
46
+ Changelog = "https://github.com/teymuri-alireza/woninet/blob/main/CHANGELOG.md"
47
+
48
+ [project.scripts]
49
+ woninet = "woninet.main:main"
50
+
51
+ [project.optional-dependencies]
52
+ test = [
53
+ "pytest>=9.1.1,<9.2",
54
+ "httpx>=0.28.1,<0.29",
55
+ ]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,151 @@
1
+ from woninet.core.storage import StorageEngine
2
+ from woninet.core.models import MetricRecord
3
+ from woninet.core.alerts import AlertRule, AlertEngine
4
+
5
+
6
+ def test_latency_trigger_alert_event(db_session):
7
+ storage = StorageEngine(session_factory=lambda: db_session)
8
+
9
+ metric = MetricRecord(device_ip="192.168.1.10", metric="latency", value=200)
10
+ storage.store_metric(metric)
11
+
12
+ alert_rule = {"metric": "latency", "threshold": 100, "consecutive_checks": 0}
13
+
14
+ alert = AlertEngine(
15
+ storage=storage,
16
+ rules=[
17
+ AlertRule(
18
+ alert_rule["metric"],
19
+ alert_rule["threshold"],
20
+ alert_rule["consecutive_checks"],
21
+ )
22
+ ],
23
+ )
24
+
25
+ alert.evaluate(
26
+ ip="192.168.1.10",
27
+ metrics_list=[
28
+ MetricRecord(device_ip="192.168.1.10", metric="latency", value=200),
29
+ ],
30
+ default_consecutive_checks={
31
+ "latency": 0,
32
+ },
33
+ )
34
+
35
+ latency = storage.get_recent_alert_events()[0]
36
+
37
+ assert latency.event_type == "trigger"
38
+
39
+
40
+ def test_packet_loss_trigger_alert_event(db_session):
41
+ storage = StorageEngine(session_factory=lambda: db_session)
42
+
43
+ metric = MetricRecord(device_ip="192.168.1.10", metric="packet_loss", value=0.5)
44
+ storage.store_metric(metric)
45
+
46
+ alert_rule = {"metric": "packet_loss", "threshold": 0.0, "consecutive_checks": 0}
47
+
48
+ alert = AlertEngine(
49
+ storage=storage,
50
+ rules=[
51
+ AlertRule(
52
+ alert_rule["metric"],
53
+ alert_rule["threshold"],
54
+ alert_rule["consecutive_checks"],
55
+ )
56
+ ],
57
+ )
58
+
59
+ alert.evaluate(
60
+ ip="192.168.1.10",
61
+ metrics_list=[
62
+ MetricRecord(device_ip="192.168.1.10", metric="packet_loss", value=0.5),
63
+ ],
64
+ default_consecutive_checks={
65
+ "packet_loss": 0,
66
+ },
67
+ )
68
+
69
+ packet_loss = storage.get_recent_alert_events()[0]
70
+
71
+ assert packet_loss.event_type == "trigger"
72
+
73
+
74
+ def test_latency_recover_alert_event(db_session):
75
+ storage = StorageEngine(session_factory=lambda: db_session)
76
+
77
+ metric = MetricRecord(device_ip="192.168.1.10", metric="latency", value=20)
78
+ storage.store_metric(metric)
79
+
80
+ state = storage.get_or_create_alert_state(
81
+ ip="192.168.1.10", metric="latency", consecutive_checks=0
82
+ )
83
+ state.state = "warning"
84
+ storage.update_alert_state(state)
85
+
86
+ alert_rule = {"metric": "latency", "threshold": 100, "consecutive_checks": 0}
87
+
88
+ alert = AlertEngine(
89
+ storage=storage,
90
+ rules=[
91
+ AlertRule(
92
+ alert_rule["metric"],
93
+ alert_rule["threshold"],
94
+ alert_rule["consecutive_checks"],
95
+ )
96
+ ],
97
+ )
98
+
99
+ alert.evaluate(
100
+ ip="192.168.1.10",
101
+ metrics_list=[
102
+ MetricRecord(device_ip="192.168.1.10", metric="latency", value=20),
103
+ ],
104
+ default_consecutive_checks={
105
+ "latency": 0,
106
+ },
107
+ )
108
+
109
+ latency = storage.get_recent_alert_events()[0]
110
+
111
+ assert latency.event_type == "recover"
112
+
113
+
114
+ def test_packet_loss_recover_alert_event(db_session):
115
+ storage = StorageEngine(session_factory=lambda: db_session)
116
+
117
+ metric = MetricRecord(device_ip="192.168.1.10", metric="packet_loss", value=0.0)
118
+ storage.store_metric(metric)
119
+
120
+ state = storage.get_or_create_alert_state(
121
+ ip="192.168.1.10", metric="packet_loss", consecutive_checks=0
122
+ )
123
+ state.state = "warning"
124
+ storage.update_alert_state(state)
125
+
126
+ alert_rule = {"metric": "packet_loss", "threshold": 0.0, "consecutive_checks": 0}
127
+
128
+ alert = AlertEngine(
129
+ storage=storage,
130
+ rules=[
131
+ AlertRule(
132
+ alert_rule["metric"],
133
+ alert_rule["threshold"],
134
+ alert_rule["consecutive_checks"],
135
+ )
136
+ ],
137
+ )
138
+
139
+ alert.evaluate(
140
+ ip="192.168.1.10",
141
+ metrics_list=[
142
+ MetricRecord(device_ip="192.168.1.10", metric="packet_loss", value=0.0),
143
+ ],
144
+ default_consecutive_checks={
145
+ "packet_loss": 0,
146
+ },
147
+ )
148
+
149
+ packet_loss = storage.get_recent_alert_events()[0]
150
+
151
+ assert packet_loss.event_type == "recover"
@@ -0,0 +1,10 @@
1
+ def test_dashboard_endpoint(client):
2
+ response = client.get("/")
3
+ assert response.status_code == 200
4
+
5
+
6
+ def test_devices_api_returns_mocked_devices(client):
7
+ response = client.get("/api/devices")
8
+
9
+ assert response.status_code == 200
10
+ assert response.json()["devices"][0]["ip"] == "192.168.1.10"