Skip to content

Commit

Permalink
Drop bottle for testing, drop python 3.6
Browse files Browse the repository at this point in the history
  • Loading branch information
infothrill committed Mar 18, 2022
1 parent e41586e commit a2e6a2c
Show file tree
Hide file tree
Showing 9 changed files with 74 additions and 201 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: [3.6, 3.7, 3.8, 3.9]
python-version: ["3.7", "3.8", "3.9", "3.10"]

steps:
- uses: actions/checkout@v3
Expand Down
2 changes: 2 additions & 0 deletions CHANGELOG.rst
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ Release history
0.6.x (unreleased)
++++++++++++++++++
- changed: moved to github actions instead of travis-ci due to policy changes on travis-ci
- changed: migrated testing from using bottle servers to mocking
- changed: dropped support for python 3.6

0.6.1 (April 2nd 2021)
++++++++++++++++++++++
Expand Down
88 changes: 20 additions & 68 deletions dyndnsc/tests/updater/test_afraid.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,94 +3,46 @@
"""Tests for afraid."""

import unittest
from multiprocessing import Process
from random import randint
from time import sleep

from bottle import Bottle, run, response, request
import responses


class AfraidApp(Bottle):
"""A minimal http server that resembles an actual freedns.afraid.org service."""

def __init__(self, host="localhost", port=8000):
"""Initiliaze."""
super(AfraidApp, self).__init__()
self.host = host
self.port = port
self.process = None
self.route(path="/api/", callback=self.api)
self.route(path="/dynamic/update.php", callback=self.update)

def api(self):
"""Return fake api response. Implemented as a bottle callback."""
arg_action = request.query.action
arg_sha = request.query.sha
assert arg_action == "getdyndns" # noqa: @assert_used
assert len(arg_sha) > 0 # noqa: @assert_used
response.content_type = "text/plain; charset=utf-8"
return """dummyhostname.example.com|127.0.0.2|%s/dynamic/update.php?sdvnkdnvv\r\n""" % self.url

def update(self):
"""Return fake update response. Implemented as a bottle callback."""
response.content_type = "text/plain; charset=utf-8"
# sample text as returned after a successful update:
return """Updated 1 host(s) foo.example.com to 127.0.0.1 in 0.178 seconds"""

def _bottle_run(self, debug=False, quiet=True):
run(self, host=self.host, port=self.port, debug=debug, quiet=quiet)

def start(self):
"""Start the server subprocess."""
self.process = Process(target=self._bottle_run)
self.process.start()
# even though I have a super fast quad core cpu, this is not working
# consistently if we don't sleep here!
sleep(4.5)

def stop(self):
"""Start the server subprocess."""
self.process.terminate()
self.process = None

@property
def url(self):
"""Return URL of this server."""
return "http://%s:%s" % (self.host, str(self.port))


class TestAfraidBottleServer(unittest.TestCase):
class TestAfraid(unittest.TestCase):
"""Test cases for Afraid."""

def setUp(self):
"""Start local test server."""
portnumber = randint(8000, 8900) # noqa: S311
self.server = AfraidApp("127.0.0.1", portnumber)
self.url = "http://127.0.0.1:%i/api/" % portnumber
self.server.start()
"""Run setup."""
responses.add(
responses.GET,
"https://freedns.afraid.org/dynamic/update.php?sdvnkdnvv",
body="Updated 1 host(s) foo.example.com to 127.0.0.1 in 0.178 seconds",
headers={"Content-Type": "text/plain; charset=utf-8"}
)
from responses import matchers
responses.add(
responses.GET,
"https://freedns.afraid.org/api/",
match=[matchers.query_string_matcher("action=getdyndns&sha=8637d0e37ad5ec987709e5bd868131d6cf972f69")],
body="dummyhostname.example.com|127.0.0.2|https://freedns.afraid.org/dynamic/update.php?sdvnkdnvv\r\n",
headers={"Content-Type": "text/plain; charset=utf-8"}
)
unittest.TestCase.setUp(self)

def tearDown(self):
"""Stop local server."""
self.server.stop()
self.server = None
"""Teardown."""
unittest.TestCase.tearDown(self)

@responses.activate
def test_afraid(self):
"""Run tests."""
from dyndnsc.updater import afraid
NAME = "afraid"
options = {
"hostname": "dummyhostname.example.com",
"userid": "dummy",
"password": "1234",
"url": self.url
"password": "1234"
}
self.assertEqual(NAME, afraid.UpdateProtocolAfraid.configuration_key)
updater = afraid.UpdateProtocolAfraid(**options)
res = updater.update()
self.assertEqual("127.0.0.1", res)


if __name__ == "__main__":
AfraidApp("localhost", 8000).run(debug=True, quiet=False)
88 changes: 24 additions & 64 deletions dyndnsc/tests/updater/test_duckdns.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,95 +3,55 @@
"""Tests for duckdns."""

import unittest
from time import sleep
from multiprocessing import Process
from random import randint

from bottle import Bottle, run, response, request
import responses


def nicupdate():
"""Return fake update response. Implemented as a bottle callback."""
# print(dict(request.query))
arg_hostname = request.query.domains
arg_token = request.query.token
arg_myip = request.query.ip
assert len(arg_hostname) > 0 # noqa: @assert_used
# duckdns doesn't want fqdns:
assert "." not in arg_hostname, "hostname must not contain dots" # noqa: @assert_used
assert len(arg_token) > 0 # noqa: @assert_used
# duckdns allows empty IP:
assert len(arg_myip) >= 0 # noqa: @assert_used
response.content_type = "text/plain; charset=utf-8"
return str("OK %s" % arg_myip)


class DuckdnsApp(Bottle):
"""Minimal http server that resembles an actual duckdns service."""

def __init__(self, host="localhost", port=8000):
"""Initialize."""
super(DuckdnsApp, self).__init__()
self.host = host
self.port = port
self.process = None
self.route(path="/update", callback=nicupdate)

def _bottle_run(self):
run(self, host=self.host, port=self.port, debug=False, quiet=True)

def start(self):
"""Start the server subprocess."""
self.process = Process(target=self._bottle_run)
self.process.start()
# even though I have a super fast quad core cpu, this is not working
# consistently if we don't sleep here!
sleep(3.5)

def stop(self):
"""Stop the server subprocess."""
self.process.terminate()
self.process = None
# sleep(1)


class TestDuckdnsBottleServer(unittest.TestCase):
class TestDuckdns(unittest.TestCase):
"""Test cases for Duckdns."""

def setUp(self):
"""Start local server."""
portnumber = randint(8000, 8900) # noqa: S311
self.server = DuckdnsApp("127.0.0.1", portnumber)
self.url = "http://127.0.0.1:%i/update" % portnumber
self.server.start()
"""Run setup."""
from responses import matchers
responses.add(
responses.GET,
"https://www.duckdns.org/update",
match=[matchers.query_string_matcher("domains=duckdns&token=dummy&ip=127.0.0.1")],
body="OK 127.0.0.1",
status=200,
headers={"Content-Type": "text/plain; charset=utf-8"}
)
responses.add(
responses.GET,
"https://www.duckdns.org/update",
match=[matchers.query_string_matcher("domains=duckdns&token=dummy&ip=")],
body="OK",
status=200,
headers={"Content-Type": "text/plain; charset=utf-8"}
)
unittest.TestCase.setUp(self)

def tearDown(self):
"""Stop local server."""
self.server.stop()
self.server = None
"""Teardown."""
unittest.TestCase.tearDown(self)

@responses.activate
def test_duckdns(self):
"""Run tests for duckdns."""
from dyndnsc.updater import duckdns
NAME = "duckdns"
self.assertEqual(
NAME, duckdns.UpdateProtocolDuckdns.configuration_key)

theip = "127.0.0.1"
options = {
"hostname": "duckdns.example.com",
"token": "dummy",
"url": self.url
"url": "https://www.duckdns.org/update"
}
updater = duckdns.UpdateProtocolDuckdns(**options)
# normal IP test:
theip = "127.0.0.1"
self.assertEqual(theip, updater.update(theip))

# empty/no IP test:
self.assertEqual(None, updater.update(None))


if __name__ == "__main__":
DuckdnsApp("localhost", 8000).run()
71 changes: 15 additions & 56 deletions dyndnsc/tests/updater/test_dyndns2.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,69 +3,32 @@
"""Tests for dyndns2."""

import unittest
from time import sleep
from multiprocessing import Process
from random import randint

from bottle import Bottle, run, response, request
import responses


def nicupdate():
"""Return fake update response. Implemented as a bottle callback."""
arg_hostname = request.query.hostname
arg_myip = request.query.myip
assert len(arg_hostname) > 0 # noqa: @assert_used
assert len(arg_myip) > 0 # noqa: @assert_used
response.content_type = "text/plain; charset=utf-8"
return str("good %s" % arg_myip)


class Dyndns2App(Bottle):
"""A minimal http server that resembles an actual dyndns service."""

def __init__(self, host="localhost", port=8000):
"""Initialize."""
super(Dyndns2App, self).__init__()
self.host = host
self.port = port
self.process = None
self.route(path="/nic/update", callback=nicupdate)

def _bottle_run(self):
run(self, host=self.host, port=self.port, debug=False, quiet=True)

def start(self):
"""Start the server subprocess."""
self.process = Process(target=self._bottle_run)
self.process.start()
# even though I have a super fast quad core cpu, this is not working
# consistently if we don't sleep here!
sleep(3.5)

def stop(self):
"""Stop the server subprocess."""
self.process.terminate()
self.process = None
# sleep(1)


class TestDyndns2BottleServer(unittest.TestCase):
class TestDyndns2(unittest.TestCase):
"""Test cases for Dyndns2."""

def setUp(self):
"""Start local server."""
portnumber = randint(8000, 8900) # noqa: S311
self.server = Dyndns2App("127.0.0.1", portnumber)
self.url = "http://127.0.0.1:%i/nic/update" % portnumber
self.server.start()
"""Run setup."""
self.url = "https://dyndns.example.com/nic/update"
from responses import matchers
responses.add(
responses.GET,
self.url,
match=[matchers.query_string_matcher("myip=127.0.0.1&hostname=dyndns.example.com")],
body="good 127.0.0.1",
status=200,
headers={"Content-Type": "text/plain; charset=utf-8"}
)
unittest.TestCase.setUp(self)

def tearDown(self):
"""Stop local server."""
self.server.stop()
self.server = None
"""Teardown."""
unittest.TestCase.tearDown(self)

@responses.activate
def test_dyndns2(self):
"""Run tests."""
from dyndnsc.updater import dyndns2
Expand All @@ -83,7 +46,3 @@ def test_dyndns2(self):
updater = dyndns2.UpdateProtocolDyndns2(**options)
res = updater.update(theip)
self.assertEqual(theip, res)


if __name__ == "__main__":
Dyndns2App("localhost", 8000).run()
4 changes: 2 additions & 2 deletions dyndnsc/updater/afraid.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,9 +69,9 @@ def compute_auth_key(userid, password):
"""
import sys
if sys.version_info >= (3, 0):
return hashlib.sha1(b"|".join((userid.encode("ascii"), # noqa: S303
return hashlib.sha1(b"|".join((userid.encode("ascii"), # noqa: S303, S324
password.encode("ascii")))).hexdigest()
return hashlib.sha1("|".join((userid, password))).hexdigest() # noqa: S303
return hashlib.sha1("|".join((userid, password))).hexdigest() # noqa: S303, S324


def records(credentials, url="https://freedns.afraid.org/api/"):
Expand Down
2 changes: 1 addition & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@
# package from pypi. Some functions are only enabled if certain packages
# are installed, so we install them here.
dnsimple-dyndns==0.1
coverage==6.2
coverage==6.3.2
10 changes: 5 additions & 5 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,10 +41,10 @@
"Operating System :: POSIX :: Linux",
"Operating System :: POSIX :: BSD",
"Programming Language :: Python",
"Programming Language :: Python :: 3.6",
"Programming Language :: Python :: 3.7",
"Programming Language :: Python :: 3.8",
"Programming Language :: Python :: 3.9"
"Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10"
]

INSTALL_REQUIRES = [
Expand All @@ -57,9 +57,9 @@
]

TESTS_REQUIRE = [
"bottle>=0.12.19",
"pytest>=4.0.0,<7.0.0",
"pytest>=4.0.0",
"pytest-console-scripts",
"responses>=0.19.0"
]

EXTRAS_REQUIRE = {}
Expand Down Expand Up @@ -91,7 +91,7 @@
keywords="dynamic dns dyndns",
url="https://github.com/infothrill/python-dyndnsc",
# https://packaging.python.org/tutorials/distributing-packages/#python-requires
python_requires=">=3.6",
python_requires=">=3.7",
setup_requires=["pytest-runner"],
install_requires=INSTALL_REQUIRES,
extras_require=EXTRAS_REQUIRE,
Expand Down

0 comments on commit a2e6a2c

Please sign in to comment.