Skip to content

Commit

Permalink
Complete test coverage, overall overhaul (janw#15)
Browse files Browse the repository at this point in the history
* Have actual testcov shown (exclude test_* files)
* Add VCRpy for testing of communications 📼
* Adjust formatting
* Unittests, coverage
* Fix codeclimate config
  • Loading branch information
janw authored Mar 2, 2019
1 parent 5f2ae72 commit 0743f9d
Show file tree
Hide file tree
Showing 12 changed files with 265 additions and 8 deletions.
5 changes: 5 additions & 0 deletions .codeclimate.yml
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
version: "2"
checks:
method-complexity:
config:
threshold: 10
plugins:
fixme:
enabled: true
Expand Down
11 changes: 11 additions & 0 deletions .coveragerc
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
[run]
omit =
tests/*
venv/*

[report]
# Regexes for lines to exclude from consideration
exclude_lines =
# Have to re-enable the standard pragma
pragma: no cover
if __name__ == .__main__.:
5 changes: 3 additions & 2 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,16 @@ cache: pip

install:
- pip install pytest pytest-cov
- if [[ $TRAVIS_PYTHON_VERSION == 3.6 ]] && [[ "$TEST_CMD" == "pytest" ]]; then
- if [[ $TRAVIS_PYTHON_VERSION == 3.6 ]] && [[ "$TEST_CMD" == pytest* ]]; then
pip install coveralls; fi
- if [[ $TEST_CMD == black* ]]; then pip install black; fi
- if [[ $TEST_CMD == flake8* ]]; then pip install flake8 flake8-bugbear
flake8-docstrings flake8-todo flake8-eradicate; fi
- pip install -r requirements.txt
- pip install -r requirements-tests.txt

env:
- TEST_CMD="pytest"
- TEST_CMD="pytest --vcr-record=none"

script:
- $TEST_CMD
Expand Down
15 changes: 11 additions & 4 deletions piholeinflux.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,10 +47,10 @@ def send_msg(influxdb, resp, name):
influxdb.write_points(json_body)


def main():
def main(single_run=False):
"""Main application daemon."""
config = ConfigParser()
config.read(path.join(HERE, "config.ini"))
config.read_file(open(path.join(HERE, "config.ini")))

influxdb_server = config["influxdb"].get("hostname", "127.0.0.1")
influxdb_port = config["influxdb"].getint("port", 8086)
Expand All @@ -66,8 +66,12 @@ def main():
influxdb_password,
influxdb_database,
)
piholes = [
Pihole(config[section])
for section in config.sections()
if section not in ("influxdb", "defaults")
]

piholes = [Pihole(config[s]) for s in config.sections() if s != "influxdb"]
while True:
try:
for pi in piholes:
Expand All @@ -83,7 +87,10 @@ def main():
print(traceback.format_exc())
sys.exit(1)

sleep(reporting_interval)
if single_run:
break
else:
sleep(reporting_interval) # pragma: no cover


if __name__ == "__main__":
Expand Down
4 changes: 4 additions & 0 deletions requirements-tests.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
pytest>=4.3.0
pytest-vcr>=1.0.1
pytest-cov>=2.6.1
pytest-mock>=1.10.1
7 changes: 5 additions & 2 deletions setup.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,8 @@ max-complexity = 18
[tool:pytest]
testpaths = tests
norecursedirs = .git venv __pycache__
addopts = -ra -q --cov . --cov-report html
--cov-report term:skip-covered
addopts =
--cov-config .coveragerc
--cov .
--cov-report html
--cov-report term
49 changes: 49 additions & 0 deletions tests/cassettes/test_main.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
interactions:
- request:
body: null
headers:
Accept: ['*/*']
Accept-Encoding: ['gzip, deflate']
Connection: [keep-alive]
User-Agent: [python-requests/2.21.0]
method: GET
uri: http://127.0.0.1:8080/admin/api.php
response:
body: {string: '{"domains_being_blocked":109200,"dns_queries_today":27686,"ads_blocked_today":2720,"ads_percentage_today":9.82446,"unique_domains":4284,"queries_forwarded":8135,"queries_cached":16831,"clients_ever_seen":34,"unique_clients":26,"dns_queries_all_types":27686,"reply_NODATA":5577,"reply_NXDOMAIN":1239,"reply_CNAME":6911,"reply_IP":11253,"privacy_level":0,"status":"enabled","gravity_last_updated":{"file_exists":true,"absolute":1550974989,"relative":{"days":"6","hours":"16","minutes":"38"}}}'}
headers:
Cache-Control: ['no-store, no-cache, must-revalidate']
Content-Length: ['490']
Content-type: [application/json]
Date: ['Sat, 02 Mar 2019 19:01:53 GMT']
Expires: ['Thu, 19 Nov 1981 08:52:00 GMT']
Pragma: [no-cache]
Server: [lighttpd/1.4.45]
Set-Cookie: [PHPSESSID=unjjrt3h08nqfddheadps7ngv4; path=/]
X-Frame-Options: [DENY]
X-Pi-hole: [The Pi-hole Web interface is working!]
status: {code: 200, message: OK}
- request:
body: 'pihole,host=pihole ads_blocked_today=2720i,ads_percentage_today=9.82446,clients_ever_seen=34i,dns_queries_all_types=27686i,dns_queries_today=27686i,domains_being_blocked=109200i,privacy_level=0i,queries_cached=16831i,queries_forwarded=8135i,reply_CNAME=6911i,reply_IP=11253i,reply_NODATA=5577i,reply_NXDOMAIN=1239i,status="enabled",unique_clients=26i,unique_domains=4284i
'
headers:
Accept: [text/plain]
Accept-Encoding: ['gzip, deflate']
Authorization: [Basic cGlob2xlOmFsbHRob3Nlc3dlZXR1bXN0YXRpc3RpY3M=]
Connection: [keep-alive]
Content-Length: ['372']
Content-Type: [application/octet-stream]
User-Agent: [python-requests/2.21.0]
method: POST
uri: http://10.10.10.1:8086/write?db=pihole
response:
body: {string: ''}
headers:
Content-Type: [application/json]
Date: ['Sat, 02 Mar 2019 19:01:53 GMT']
Request-Id: [a4b98c69-3d1d-11e9-9988-0242ac12000a]
X-Influxdb-Build: [OSS]
X-Influxdb-Version: [1.7.4]
X-Request-Id: [a4b98c69-3d1d-11e9-9988-0242ac12000a]
status: {code: 204, message: No Content}
version: 1
49 changes: 49 additions & 0 deletions tests/cassettes/test_main_instance_name.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
interactions:
- request:
body: null
headers:
Accept: ['*/*']
Accept-Encoding: ['gzip, deflate']
Connection: [keep-alive]
User-Agent: [python-requests/2.21.0]
method: GET
uri: http://127.0.0.1:8080/admin/api.php
response:
body: {string: '{"domains_being_blocked":109200,"dns_queries_today":28188,"ads_blocked_today":2940,"ads_percentage_today":10.42997,"unique_domains":4296,"queries_forwarded":8269,"queries_cached":16979,"clients_ever_seen":34,"unique_clients":26,"dns_queries_all_types":28188,"reply_NODATA":5630,"reply_NXDOMAIN":1242,"reply_CNAME":6985,"reply_IP":11592,"privacy_level":0,"status":"enabled","gravity_last_updated":{"file_exists":true,"absolute":1550974989,"relative":{"days":"6","hours":"16","minutes":"51"}}}'}
headers:
Cache-Control: ['no-store, no-cache, must-revalidate']
Content-Length: ['491']
Content-type: [application/json]
Date: ['Sat, 02 Mar 2019 19:14:37 GMT']
Expires: ['Thu, 19 Nov 1981 08:52:00 GMT']
Pragma: [no-cache]
Server: [lighttpd/1.4.45]
Set-Cookie: [PHPSESSID=0j7nh0gmcej13ce5pt23pqttb5; path=/]
X-Frame-Options: [DENY]
X-Pi-hole: [The Pi-hole Web interface is working!]
status: {code: 200, message: OK}
- request:
body: 'pihole,host=testinstance ads_blocked_today=2940i,ads_percentage_today=10.42997,clients_ever_seen=34i,dns_queries_all_types=28188i,dns_queries_today=28188i,domains_being_blocked=109200i,privacy_level=0i,queries_cached=16979i,queries_forwarded=8269i,reply_CNAME=6985i,reply_IP=11592i,reply_NODATA=5630i,reply_NXDOMAIN=1242i,status="enabled",unique_clients=26i,unique_domains=4296i
'
headers:
Accept: [text/plain]
Accept-Encoding: ['gzip, deflate']
Authorization: [Basic cGlob2xlOmFsbHRob3Nlc3dlZXR1bXN0YXRpc3RpY3M=]
Connection: [keep-alive]
Content-Length: ['379']
Content-Type: [application/octet-stream]
User-Agent: [python-requests/2.21.0]
method: POST
uri: http://10.10.10.1:8086/write?db=pihole
response:
body: {string: ''}
headers:
Content-Type: [application/json]
Date: ['Sat, 02 Mar 2019 19:14:37 GMT']
Request-Id: [6bfbbdb4-3d1f-11e9-99a7-0242ac12000a]
X-Influxdb-Build: [OSS]
X-Influxdb-Version: [1.7.4]
X-Request-Id: [6bfbbdb4-3d1f-11e9-99a7-0242ac12000a]
status: {code: 204, message: No Content}
version: 1
25 changes: 25 additions & 0 deletions tests/cassettes/test_pihole_get_data.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
interactions:
- request:
body: null
headers:
Accept: ['*/*']
Accept-Encoding: ['gzip, deflate']
Connection: [keep-alive]
User-Agent: [python-requests/2.21.0]
method: GET
uri: http://127.0.0.1:8080/admin/api.php
response:
body: {string: '{"domains_being_blocked":109200,"dns_queries_today":18596,"ads_blocked_today":732,"ads_percentage_today":3.93633,"unique_domains":2408,"queries_forwarded":5033,"queries_cached":12831,"clients_ever_seen":28,"unique_clients":25,"dns_queries_all_types":18596,"reply_NODATA":4457,"reply_NXDOMAIN":720,"reply_CNAME":4846,"reply_IP":8123,"privacy_level":0,"status":"enabled","gravity_last_updated":{"file_exists":true,"absolute":1550974989,"relative":{"days":"1","hours":"17","minutes":"28"}}}'}
headers:
Cache-Control: ['no-store, no-cache, must-revalidate']
Content-Length: ['487']
Content-type: [application/json]
Date: ['Mon, 25 Feb 2019 19:51:36 GMT']
Expires: ['Thu, 19 Nov 1981 08:52:00 GMT']
Pragma: [no-cache]
Server: [lighttpd/1.4.45]
Set-Cookie: [PHPSESSID=ltpa1m1eatesvl0jqi2fq6mqn5; path=/]
X-Frame-Options: [DENY]
X-Pi-hole: [The Pi-hole Web interface is working!]
status: {code: 200, message: OK}
version: 1
18 changes: 18 additions & 0 deletions tests/test_influxdb.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
from piholeinflux import send_msg


def test_send_msg(mocker):
"""Test send_msg function, sending data to influxDB."""
indata = {"some": "value", "gravity_last_updated": "should be gone"}
expected = [
{
"measurement": "pihole",
"tags": {"host": "myname"},
"fields": {"some": "value"},
}
]
mock_influx = mocker.patch("influxdb.InfluxDBClient")

send_msg(mock_influx(), indata, "myname")

mock_influx().write_points.assert_called_once_with(expected)
71 changes: 71 additions & 0 deletions tests/test_main.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import io
from os import path

import pytest
from piholeinflux import main

CONFIG_FILE_CONTENT = """
[defaults]
request_timeout = 10
reporting_interval = 30
[influxdb]
port = 8086
hostname = 10.10.10.1
username = pihole
password = allthosesweetumstatistics
database = pihole
[pihole]
api_location = http://127.0.0.1:8080/admin/api.php
"""

CONFIG_FILE_CONTENT_INSTANCE_NAME = (
CONFIG_FILE_CONTENT + "instance_name = testinstance\n"
)
HERE = path.dirname(path.dirname(path.realpath(__file__)))


@pytest.mark.vcr()
def test_main(mocker):
"""Test main function executed when running the daemon."""
mock_config = mocker.patch(
"builtins.open", return_value=io.StringIO(CONFIG_FILE_CONTENT)
)

with mock_config:
main(single_run=True)

mock_config.assert_called_with(path.join(HERE, "config.ini"))


@pytest.mark.vcr()
def test_main_instance_name(mocker):
"""Test main function executed, configured with instanec_name."""
mock_config = mocker.patch(
"builtins.open", return_value=io.StringIO(CONFIG_FILE_CONTENT_INSTANCE_NAME)
)

with mock_config:
main(single_run=True)

mock_config.assert_called_with(path.join(HERE, "config.ini"))


@pytest.mark.vcr()
def test_main_exception(mocker):
"""Test main function, failing with exceptino inside of loop."""
mock_config = mocker.patch(
"builtins.open", return_value=io.StringIO(CONFIG_FILE_CONTENT)
)
mock_get_data = mocker.patch(
"piholeinflux.Pihole.get_data", side_effect=ConnectionError
)

with mock_config:
with pytest.raises(SystemExit) as ctx:
main(single_run=True)

assert ctx.value.code == 1
mock_get_data.assert_called_once_with()
14 changes: 14 additions & 0 deletions tests/test_pihole.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import pytest
from piholeinflux import Pihole


Expand All @@ -14,3 +15,16 @@ def test_pihole_init():
assert pihole.name == "here.example"
assert pihole.url == "http://here.example"
assert pihole.timeout == 10


@pytest.mark.vcr()
def test_pihole_get_data():
"""Test getting data from an API endpoint."""
config = {"api_location": "http://127.0.0.1:8080/admin/api.php"}

pihole = Pihole(config)

response = pihole.get_data()
assert "domains_being_blocked" in response
assert "ads_percentage_today" in response
assert "gravity_last_updated" in response

0 comments on commit 0743f9d

Please sign in to comment.