Skip to content

Commit f6865b1

Browse files
committed
test: wrap up EXAMPLE sweep for openvpn-client-list and logfile
Two plugins were left over from the EXAMPLE sweep because neither had a working unit test to modernize: - **openvpn-client-list**: its `--test` branch just printed `TODO` and never consumed the fixture. Rewired the branch to parse the OpenVPN status file from the fixture content the same way the `--filename` branch parses it from disk. Added a declarative `TESTS` list with three cases (default ok, warn above threshold, crit above threshold) against the renamed `five-clients-four-users` fixture. Bumped `__version__`. - **logfile**: no run file existed at all. The plugin has no `--test` hook either - it reads a real file from `--filename` and keeps a per-file SQLite cache under `/tmp` to track the read offset between runs. The new unit-test drives it via `--filename=stdout/<fixture>` with four scenarios (`django-app-clean`, `-two-warnings`, `-one-warning-one-error`, plus an `--ignore-pattern` variant). The test method deletes the per-fixture cache DB before each sub-test so each scenario always starts from offset 0. The orphaned, `.log`-gitignored `EXAMPLE01.log` sample that was never part of a committed test has been removed from the local checkout. After this commit there are no `EXAMPLE*` fixtures left under `check-plugins/*/unit-test/stdout/` anywhere in the repository.
1 parent b8ea495 commit f6865b1

File tree

6 files changed

+213
-18
lines changed

6 files changed

+213
-18
lines changed
Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
#!/usr/bin/env python3
2+
# -*- coding: utf-8; py-indent-offset: 4 -*-
3+
#
4+
# Author: Linuxfabrik GmbH, Zurich, Switzerland
5+
# Contact: info (at) linuxfabrik (dot) ch
6+
# https://www.linuxfabrik.ch/
7+
# License: The Unlicense, see LICENSE file.
8+
9+
# https://github.com/Linuxfabrik/monitoring-plugins/blob/main/CONTRIBUTING.md
10+
11+
"""Tests for the logfile check plugin.
12+
13+
The plugin has no `--test` hook because it reads a real file given via
14+
`--filename`. The test scenarios pass the fixture paths directly as
15+
`--filename` values. The plugin also maintains a per-file SQLite cache
16+
under `/tmp/linuxfabrik-monitoring-plugins-logfile-<basename>.db` to
17+
track the last read offset, so each subtest deletes its own cache DB
18+
first to ensure a clean "first scan" state.
19+
"""
20+
21+
import os
22+
import sys
23+
import tempfile
24+
import unittest
25+
26+
sys.path.insert(0, '..')
27+
28+
from lib.globals import STATE_CRIT, STATE_OK, STATE_WARN
29+
import lib.lftest
30+
31+
32+
def _db_path(fixture_basename):
33+
return os.path.join(
34+
tempfile.gettempdir(),
35+
f'linuxfabrik-monitoring-plugins-logfile-{fixture_basename}.db',
36+
)
37+
38+
39+
# The `test` field is a dummy (the plugin has no `--test` hook) but it
40+
# is required by lib.lftest.run. `params` carries the real CLI, built
41+
# from `--filename=stdout/<fixture>` plus whatever pattern/threshold
42+
# flags the scenario needs.
43+
TESTS = [
44+
{
45+
'id': 'ok-clean-log-no-matches',
46+
'test': 'stdout/django-app-clean,,0',
47+
'params': (
48+
'--filename=stdout/django-app-clean '
49+
'--warning-regex=" WARNING " '
50+
'--critical-regex=" ERROR "'
51+
),
52+
'assert-retc': STATE_OK,
53+
'assert-in': ['Scanned 5 lines'],
54+
},
55+
{
56+
'id': 'warn-two-warnings',
57+
'test': 'stdout/django-app-two-warnings,,0',
58+
'params': (
59+
'--filename=stdout/django-app-two-warnings '
60+
'--warning-regex=" WARNING " '
61+
'--critical-regex=" ERROR "'
62+
),
63+
'assert-retc': STATE_WARN,
64+
'assert-in': [
65+
'Scanned 4 lines',
66+
'2 warning matches',
67+
'Not Found: /favicon.ico',
68+
'Not Found: /robots.txt',
69+
],
70+
},
71+
{
72+
'id': 'crit-one-warning-one-error',
73+
'test': 'stdout/django-app-one-warning-one-error,,0',
74+
'params': (
75+
'--filename=stdout/django-app-one-warning-one-error '
76+
'--warning-regex=" WARNING " '
77+
'--critical-regex=" ERROR "'
78+
),
79+
'assert-retc': STATE_CRIT,
80+
'assert-in': [
81+
'Scanned 3 lines',
82+
'1 warning match',
83+
'1 critical match',
84+
'MYPRODUCT salutation sync failed',
85+
],
86+
},
87+
{
88+
'id': 'ok-one-warning-suppressed-via-ignore-pattern',
89+
'test': 'stdout/django-app-two-warnings,,0',
90+
'params': (
91+
'--filename=stdout/django-app-two-warnings '
92+
'--warning-regex=" WARNING " '
93+
'--critical-regex=" ERROR " '
94+
'--ignore-pattern=favicon.ico '
95+
'--ignore-pattern=robots.txt'
96+
),
97+
'assert-retc': STATE_OK,
98+
'assert-in': ['Scanned 4 lines'],
99+
'assert-not-in': ['warning matches', 'critical matches'],
100+
},
101+
]
102+
103+
104+
class TestCheck(unittest.TestCase):
105+
106+
check = '../logfile'
107+
108+
def test(self):
109+
for t in TESTS:
110+
with self.subTest(id=t['id']):
111+
# Delete the stateful cache DB for this fixture so the
112+
# test is reproducible and always starts from offset 0.
113+
fixture_basename = t['params'].split('--filename=stdout/', 1)[1].split(' ', 1)[0]
114+
db_file = _db_path(fixture_basename)
115+
if os.path.exists(db_file):
116+
os.remove(db_file)
117+
lib.lftest.run(self, self.check, t)
118+
119+
120+
if __name__ == '__main__':
121+
unittest.main()
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
2024-11-26 13:36:23,501 [INFO] mvc_app.mvc_lib.myproduct sync_salutations(): MYPRODUCT salutation sync ok
2+
2024-11-26 13:36:24,129 [INFO] mvc_app.mvc_lib.myproduct sync_titles(): MYPRODUCT title sync ok
3+
2024-11-26 13:36:24,746 [INFO] mvc_app.mvc_lib.myproduct sync_countries(): MYPRODUCT country sync ok
4+
2024-11-26 13:36:25,190 [INFO] mvc_app.mvc_lib.myproduct sync_languages(): MYPRODUCT language sync ok
5+
2024-11-26 13:36:25,635 [INFO] mvc_app.mvc_lib.myproduct sync_users(): MYPRODUCT user sync ok
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
2024-11-26 13:31:25,048 django.request WARNING Not Found: /favicon.ico
2+
2024-11-26 13:34:58,197 mvc_app.mvc_lib.myproduct ERROR MYPRODUCT salutation sync failed: result='{\n "message":"Unauthorized"\n}'
3+
2024-11-26 13:36:23,501 [INFO] mvc_app.mvc_lib.myproduct sync_salutations(): MYPRODUCT salutation sync ok
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
2024-11-26 13:31:25,048 django.request WARNING Not Found: /favicon.ico
2+
2024-11-26 13:31:30,229 django.request WARNING Not Found: /robots.txt
3+
2024-11-26 13:36:23,501 [INFO] mvc_app.mvc_lib.myproduct sync_salutations(): MYPRODUCT salutation sync ok
4+
2024-11-26 13:36:24,129 [INFO] mvc_app.mvc_lib.myproduct sync_titles(): MYPRODUCT title sync ok

check-plugins/openvpn-client-list/openvpn-client-list

Lines changed: 19 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ import lib.txt
2020
from lib.globals import STATE_UNKNOWN
2121

2222
__author__ = 'Linuxfabrik GmbH, Zurich/Switzerland'
23-
__version__ = '2026040801'
23+
__version__ = '2026041301'
2424

2525
DESCRIPTION = """Lists all clients currently connected to an OpenVPN server by parsing the status log file. Reports client name, remote address, bytes received and sent, and connection time.\nRequires root or sudo."""
2626

@@ -91,26 +91,27 @@ def main():
9191
if args.TEST is None:
9292
try:
9393
with open(args.FILENAME, 'r') as file:
94-
counter = 0
95-
table = []
96-
for line in file:
97-
if line.startswith('CLIENT_LIST'):
98-
counter += 1
99-
line = line.split(',')
100-
table.append(
101-
{
102-
'name': line[1],
103-
'ext_ip': line[2].split(':')[0],
104-
'int_ip': line[3],
105-
'connection_time': line[7],
106-
}
107-
)
94+
lines = file.readlines()
10895
except IOError:
10996
lib.base.cu(f'Failed to read file {args.FILENAME}.')
11097
else:
111-
# do not call the command, put in test data
112-
_, _, _ = lib.lftest.test(args.TEST)
113-
lib.base.oao('TODO')
98+
stdout, _, _ = lib.lftest.test(args.TEST)
99+
lines = stdout.splitlines(keepends=True)
100+
101+
counter = 0
102+
table = []
103+
for line in lines:
104+
if line.startswith('CLIENT_LIST'):
105+
counter += 1
106+
line = line.split(',')
107+
table.append(
108+
{
109+
'name': line[1],
110+
'ext_ip': line[2].split(':')[0],
111+
'int_ip': line[3],
112+
'connection_time': line[7],
113+
}
114+
)
114115

115116
state = lib.base.get_state(counter, args.WARN, args.CRIT)
116117
perfdata = lib.base.get_perfdata(
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
#!/usr/bin/env python3
2+
# -*- coding: utf-8; py-indent-offset: 4 -*-
3+
#
4+
# Author: Linuxfabrik GmbH, Zurich, Switzerland
5+
# Contact: info (at) linuxfabrik (dot) ch
6+
# https://www.linuxfabrik.ch/
7+
# License: The Unlicense, see LICENSE file.
8+
9+
# https://github.com/Linuxfabrik/monitoring-plugins/blob/main/CONTRIBUTING.md
10+
11+
import sys
12+
sys.path.insert(0, '..')
13+
14+
import unittest
15+
16+
from lib.globals import STATE_CRIT, STATE_OK, STATE_WARN
17+
import lib.lftest
18+
19+
20+
TESTS = [
21+
{
22+
'id': 'ok-five-clients-four-users',
23+
'test': 'stdout/five-clients-four-users,,0',
24+
'assert-retc': STATE_OK,
25+
'assert-in': [
26+
'5 users connected to OpenVPN Server.',
27+
'user1@linuxfabrik.ch',
28+
'user2@linuxfabrik.ch',
29+
'user3@linuxfabrik.ch',
30+
'user4@linuxfabrik.ch',
31+
],
32+
},
33+
{
34+
'id': 'warn-above-warn-threshold',
35+
'test': 'stdout/five-clients-four-users,,0',
36+
'params': '--warning 4',
37+
'assert-retc': STATE_WARN,
38+
'assert-in': ['5 users connected to OpenVPN Server.'],
39+
},
40+
{
41+
'id': 'crit-above-crit-threshold',
42+
'test': 'stdout/five-clients-four-users,,0',
43+
'params': '--warning 3 --critical 4',
44+
'assert-retc': STATE_CRIT,
45+
'assert-in': ['5 users connected to OpenVPN Server.'],
46+
},
47+
]
48+
49+
50+
class TestCheck(unittest.TestCase):
51+
52+
check = '../openvpn-client-list'
53+
54+
def test(self):
55+
for t in TESTS:
56+
with self.subTest(id=t['id']):
57+
lib.lftest.run(self, self.check, t)
58+
59+
60+
if __name__ == '__main__':
61+
unittest.main()

0 commit comments

Comments
 (0)